Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/.vitepress/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ export default {
text: "Developer",
items: [
{ text: "Overview", link: "/dev/" },
{
text: "Real-Time Communication",
link: "/dev/realtime/",
items: [
{ text: "User Hub", link: "/dev/realtime/user-hub" },
{ text: "Share Link Hub", link: "/dev/realtime/share-link-hub" },
{ text: "Live Control", link: "/dev/realtime/live-control" },
{ text: "Types Reference", link: "/dev/realtime/types" },
],
},
{ text: "Contributing", link: "/dev/contributing/" },
{
text: "Compile firmware",
Expand Down
21 changes: 10 additions & 11 deletions docs/dev/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,19 @@ Authentication for applications is done via a API Token which are to be sent as

You can generate a API Token on the website. [New API Token UI](https://next.openshock.app/settings/api-tokens)

### WebSockets
### Real-Time Communication

There is a few different WebSocket endpoints. Most of them use json. The Hub (previously named Device) websocket uses flatbuffers binary serialization.
OpenShock provides two real-time communication methods for client applications:

GW = Gateway or LiveControlGateway (e.g. de1-gateway.openshock.app)
API = Main API (e.g. api.openshock.app)
**SignalR Hubs** - Built on Microsoft's [SignalR](https://learn.microsoft.com/aspnet/core/signalr) framework, using WebSocket transport with JSON payloads. Used for device management, control commands, event notifications, and share links.

- [GW]/1/ws/live/{deviceId} # Live Control Websocket, json
- [GW]/1/ws/device # Hubs (devices) websocket, flatbuffers
- [API]/1/ws/device # Legacy Hubs (Deprecated, not implemented anymore as of 31.08.2024)
- `[API]/1/hubs/user` - Authenticated user hub
- `[API]/1/hubs/share/link/{id}` - Public share link hub

## SignalR
**Live Control WebSocket** - A raw JSON WebSocket for continuous real-time shocker control (e.g. VR integrations).

SignalR is a Realtime Messaging Framework developed by Microsoft. The transport way we use is only WebSocket with JSON.
- `[GW]/1/ws/live/{deviceId}` - Requires authentication

- [API]/1/hubs/user
- [API]/1/hubs/share/link/{id}
Where `[API]` = `api.openshock.app` and `[GW]` = a gateway host (e.g. `de1-gateway.openshock.app`).

For full details on endpoints, methods, message formats, and types, see the [Real-Time Communication Guide](./realtime/).
30 changes: 30 additions & 0 deletions docs/dev/realtime/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

# Real-Time Communication

OpenShock provides two real-time communication channels for client applications: **SignalR hubs** for device management and control, and a **live control WebSocket** for continuous real-time shocker interaction.

All endpoints use JSON payloads over WebSocket transport.

## Authentication

Both SignalR hubs and the live control WebSocket require the following headers:

| Header | Description |
|--------|-------------|
| `User-Agent` | A meaningful identifier for your application (e.g. `MyApp/1.0`). Empty values are rejected with `403`. |
| `OpenShockToken` | API token created in [account settings](https://next.openshock.app/settings/api-tokens). |

The share link hub is an exception - see [Share Link Hub](./share-link-hub) for its authentication model.

## SignalR Hubs

- [User Hub](./user-hub) - Device management, control commands, and real-time event notifications for authenticated users.
- [Share Link Hub](./share-link-hub) - Interact with devices exposed through a public share link. Supports both authenticated and guest access.

## WebSocket

- [Live Control](./live-control) - Raw JSON WebSocket for continuous real-time shocker control (e.g. VR integrations).

## Common Types

- [Types Reference](./types) - Shared data types used across all endpoints.
58 changes: 58 additions & 0 deletions docs/dev/realtime/live-control.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@

# Live Control WebSocket

**Endpoint:** `wss://{gateway}/1/ws/live/{deviceId}?tps={tps}`

Where `{gateway}` is the gateway host (e.g. `de1-gateway.openshock.app`), `{deviceId}` is the target device UUID, and `{tps}` (optional, 1--10) sets the frames-per-second rate.

This is a raw WebSocket (not SignalR) for continuous real-time shocker control - designed for applications where low-latency streaming input is needed. Requires the `Shockers_Use` API token permission.

## Authentication

Uses the same headers as described in the [overview](./#authentication).

## Message Format

All messages are JSON. Requests from the client:

```json
{
"requestType": <integer>,
"data": <object or null>
}
```

Responses from the server:

```json
{
"responseType": <integer>,
"data": <object or null>
}
```

## Client Request Types

| Value | Name | Data | Description |
|-------|------|------|-------------|
| `0` | Frame | [LiveFrame](./types#liveframe) | Send a single shocker control frame. |
| `1` | BulkFrame | array of [LiveFrame](./types#liveframe) | Send multiple shocker control frames at once. |
| `1000` | Pong | *none* | Response to a server Ping. |

## Server Response Types

| Value | Name | Data | Description |
|-------|------|------|-------------|
| `0` | Frame | -- | Frame acknowledged. |
| `50` | TPS | `{ "client": <integer> }` | Reports the current frames-per-second rate. |
| `100` | DeviceNotConnected | -- | The target device is offline. |
| `101` | DeviceConnected | -- | The target device has come online. |
| `150` | ShockerNotFound | -- | The referenced shocker does not exist on this device. |
| `151` | ShockerMissingLivePermission | -- | You do not have live control permission for this shocker. |
| `152` | ShockerMissingPermission | -- | You lack the required permission type for this shocker. |
| `153` | ShockerPaused | -- | The shocker is currently paused. |
| `154` | ShockerExclusive | -- | The shocker is under exclusive control by another session. |
| `200` | InvalidData | -- | The request payload was malformed. |
| `201` | RequestTypeNotFound | -- | Unrecognized request type. |
| `1000` | Ping | `{ "timestamp": <integer> }` | Server ping. Respond with a Pong. `timestamp` is Unix milliseconds. |
| `1001` | LatencyAnnounce | `{ "deviceLatency": <integer>, "ownLatency": <integer> }` | Latency info in ms. `deviceLatency` = server-to-device, `ownLatency` = client-to-server. |
39 changes: 39 additions & 0 deletions docs/dev/realtime/share-link-hub.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

# Share Link Hub

**Endpoint:** `https://api.openshock.app/1/hubs/share/link/{id}`

Replace `{id}` with the share link UUID. This hub allows interaction with devices exposed through a public share link.

## Authentication

This hub does **not** require an API token. There are two connection modes:

- **Authenticated:** Provide the `OpenShockToken` header to connect as an authenticated user.
- **Guest:** Omit the token and optionally pass a `name` query parameter to identify yourself (e.g. `?name=Guest`).

## Server Methods

### `Control`

Send control commands to shockers available on this share link. Commands are subject to the share link's permission and limit settings.

| Parameter | Type | Description |
|-----------|------|-------------|
| `shocks` | array of [Control](./types#control-object) | One or more shocker commands. |

## Client Events

### `Welcome`

Sent immediately after connecting.

| Field | Type | Description |
|-------|------|-------------|
| `authType` | string | Either `"Authenticated"` or `"Guest"`. |

### `Updated`

Fired when the share link's configuration has changed (e.g. permissions modified, shockers added/removed). The client should refresh its state when this event is received.

*No payload.*
92 changes: 92 additions & 0 deletions docs/dev/realtime/types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@

# Types Reference

Shared data types used across the SignalR hubs and live control WebSocket.

## Control Object

A command targeting a single shocker.

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | The shocker identifier. |
| `type` | [ControlType](#controltype) | The action to perform. |
| `intensity` | integer | Intensity level (0--100). |
| `duration` | integer | Duration in milliseconds (300--30000). |
| `exclusive` | boolean | If `true`, overrides any active live control session. Default `false`. |

## LiveFrame

A single live control frame for continuous real-time control.

| Field | Type | Description |
|-------|------|-------------|
| `shocker` | UUID | The shocker identifier. |
| `intensity` | integer | Intensity level (0--100). |
| `type` | string | One of `"Stop"`, `"Shock"`, `"Vibrate"`, `"Sound"`. |

## ControlType

| Value | Name |
|-------|------|
| `0` | Stop |
| `1` | Shock |
| `2` | Vibrate |
| `3` | Sound |

## DeviceOnlineState

| Field | Type | Description |
|-------|------|-------------|
| `device` | UUID | The device identifier. |
| `online` | boolean | Whether the device is connected. |
| `firmwareVersion` | string or null | Firmware version (e.g. `1.2.0`), or `null` if unknown. |

## DeviceUpdateType

| Value | Name | Description |
|-------|------|-------------|
| `0` | Created | A new device was added. |
| `1` | Updated | Device metadata changed. |
| `2` | ShockerUpdated | A shocker on the device was modified (name, limits, etc.). |
| `3` | Deleted | The device was removed. |

## OtaUpdateProgressTask

| Value | Name |
|-------|------|
| `0` | FetchingMetadata |
| `1` | PreparingForUpdate |
| `2` | FlashingFilesystem |
| `3` | VerifyingFilesystem |
| `4` | FlashingApplication |
| `5` | MarkingApplicationBootable |
| `6` | Rebooting |

## ControlLogSender

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Sender's user ID. |
| `name` | string | Sender's display name. |
| `image` | string | URL of the sender's avatar. |
| `customName` | string or null | Custom name provided via `ControlV2`, if any. |
| `connectionId` | string | SignalR connection ID of the sender. |
| `additionalItems` | object | Extra metadata (key-value pairs). |

## ControlLog

| Field | Type | Description |
|-------|------|-------------|
| `shocker` | [BasicShockerInfo](#basicshockerinfo) | The shocker that was controlled. |
| `type` | [ControlType](#controltype) | The control action that was performed. |
| `intensity` | integer | Intensity level (0--100). |
| `duration` | integer | Duration in milliseconds. |
| `executedAt` | string | ISO 8601 timestamp of execution. |

## BasicShockerInfo

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | The shocker identifier. |
| `name` | string | The shocker's display name. |
Loading
Loading