Skip to content
Draft
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
2 changes: 2 additions & 0 deletions docs/dev/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@ SignalR is a Realtime Messaging Framework developed by Microsoft. The transport

- [API]/1/hubs/user
- [API]/1/hubs/share/link/{id}

For connection examples and event details, see the [SignalR WebSockets](signalr-websockets.md) guide.
121 changes: 121 additions & 0 deletions docs/dev/signalr-websockets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
---
tags:
- developer
- api
- backend
- websocket
- signalr
---

# Using SignalR WebSockets

OpenShock exposes real-time events and control channels through [SignalR](https://learn.microsoft.com/aspnet/core/signalr). The hubs use WebSocket transport with JSON payloads.

## Endpoints

- `https://api.openshock.app/1/hubs/user`
*Receive device status, logs and OTA updates for an authenticated user and send control commands.*
- `https://api.openshock.app/1/hubs/share/link/{id}`
*Interact with a public share link. Replace `{id}` with the share link UUID.*

## Connecting

Use a SignalR client and provide the required headers:

- `User-Agent`: A meaningful identifier for your application.
- `Open-Shock-Token`: API token created in account settings.

```ts
import { HubConnectionBuilder, LogLevel } from "@microsoft/signalr";

const connection = new HubConnectionBuilder()
.withUrl("https://api.openshock.app/1/hubs/user", {
accessTokenFactory: () => "YOUR_API_TOKEN",
headers: {
"User-Agent": "MyExampleApp/1.0"
}
})
.withAutomaticReconnect()
.configureLogging(LogLevel.Information)
.build();

await connection.start();
```

## Server methods

The user hub exposes the following methods that can be invoked with `connection.invoke`:

| Method | Definition | Description |
|--------|------------|-------------|
| `Control` | `Control(IReadOnlyList<Control> shocks)` | Send one or more control commands to shockers. Each command requires `id`, `type`, `intensity`, `duration` and optional `exclusive`. |
| `ControlV2` | `ControlV2(IReadOnlyList<Control> shocks, string? customName)` | Same as `Control` but allows an optional custom sender name to appear in logs. |
| `CaptivePortal` | `CaptivePortal(Guid deviceId, bool enabled)` | Enable or disable captive portal on a device. |
| `EmergencyStop` | `EmergencyStop(Guid deviceId)` | Immediately stop a device. |
| `OtaInstall` | `OtaInstall(Guid deviceId, SemVersion version)` | Trigger firmware update for a device to a specific version. |
| `Reboot` | `Reboot(Guid deviceId)` | Reboot a device. |

Example control message:

```json
[
{
"id": "00000000-0000-0000-0000-000000000000",
"type": 1,
"intensity": 50,
"duration": 1000,
"exclusive": false
}
]
```

## Client methods

Listen for server calls with `connection.on("MethodName", handler)`.

### User hub methods

| Method | Definition | Description |
|--------|------------|-------------|
| `Welcome` | `Welcome(string connectionId)` | Fired after connecting and returns the SignalR connection ID. |
| `DeviceStatus` | `DeviceStatus(IList<DeviceOnlineState> deviceOnlineStates)` | Provides online status and firmware version for devices accessible to the user. |
| `Log` | `Log(ControlLogSender sender, IEnumerable<ControlLog> logs)` | Emits control log entries generated by device actions. |
| `DeviceUpdate` | `DeviceUpdate(Guid deviceId, DeviceUpdateType type)` | Notifies when a device is updated or removed. |
| `OtaInstallStarted` | `OtaInstallStarted(Guid deviceId, int updateId, SemVersion version)` | A firmware update began on the device. |
| `OtaInstallProgress` | `OtaInstallProgress(Guid deviceId, int updateId, OtaUpdateProgressTask task, float progress)` | Progress update for an ongoing firmware install. |
| `OtaInstallFailed` | `OtaInstallFailed(Guid deviceId, int updateId, bool fatal, string message)` | Firmware install failed. `fatal` indicates rollback. |
| `OtaRollback` | `OtaRollback(Guid deviceId, int updateId)` | Firmware install rolled back to previous version. |
| `OtaInstallSucceeded` | `OtaInstallSucceeded(Guid deviceId, int updateId)` | Firmware install finished successfully. |

### Public share hub methods

| Method | Definition | Description |
|--------|------------|-------------|
| `Welcome` | `Welcome(PublicShareHub.AuthType authType)` | Indicates whether the connected client is authenticated or a guest. |
| `Updated` | `Updated()` | Share link configuration changed. |

These methods deliver typed payloads matching the backend models. Refer to the [OpenShock API](https://github.com/OpenShock/API) for structure details.

## Share link hub

To connect to a share link hub, use the share link identifier and optionally provide a `name` query parameter for guest connections:

```ts
const shareConn = new HubConnectionBuilder()
.withUrl("https://api.openshock.app/1/hubs/share/link/01234567-89ab-cdef-0123-456789abcdef?name=Guest")
.build();
await shareConn.start();
```

Guests can call `Control` to interact with the devices shared via the link. Authenticated users may also be notified through the user hub when share link activity occurs.

## Disconnecting

Call `connection.stop()` when your application shuts down to gracefully close the WebSocket.

```ts
await connection.stop();
```

This page describes the basics for working with OpenShock's SignalR hubs. For full type definitions consult the server source code or OpenShock community resources.

3 changes: 2 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ nav:
- diy/hardware-buying.md
- diy/assembling.md

- Developer:
- Developer:
- dev/index.md
- SignalR WebSockets: dev/signalr-websockets.md
- Contributing:
- dev/contributing/index.md
- Software:
Expand Down