diff --git a/docs/.vitepress/sidebar.ts b/docs/.vitepress/sidebar.ts index 7d0bfa45..1479ef53 100644 --- a/docs/.vitepress/sidebar.ts +++ b/docs/.vitepress/sidebar.ts @@ -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", diff --git a/docs/dev/index.md b/docs/dev/index.md index b0b5801f..1243f83d 100644 --- a/docs/dev/index.md +++ b/docs/dev/index.md @@ -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/). diff --git a/docs/dev/realtime/index.md b/docs/dev/realtime/index.md new file mode 100644 index 00000000..5b13b266 --- /dev/null +++ b/docs/dev/realtime/index.md @@ -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. diff --git a/docs/dev/realtime/live-control.md b/docs/dev/realtime/live-control.md new file mode 100644 index 00000000..4292eee3 --- /dev/null +++ b/docs/dev/realtime/live-control.md @@ -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": , + "data": +} +``` + +Responses from the server: + +```json +{ + "responseType": , + "data": +} +``` + +## 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": }` | 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": }` | Server ping. Respond with a Pong. `timestamp` is Unix milliseconds. | +| `1001` | LatencyAnnounce | `{ "deviceLatency": , "ownLatency": }` | Latency info in ms. `deviceLatency` = server-to-device, `ownLatency` = client-to-server. | diff --git a/docs/dev/realtime/share-link-hub.md b/docs/dev/realtime/share-link-hub.md new file mode 100644 index 00000000..a79414e8 --- /dev/null +++ b/docs/dev/realtime/share-link-hub.md @@ -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.* diff --git a/docs/dev/realtime/types.md b/docs/dev/realtime/types.md new file mode 100644 index 00000000..6b644b29 --- /dev/null +++ b/docs/dev/realtime/types.md @@ -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. | diff --git a/docs/dev/realtime/user-hub.md b/docs/dev/realtime/user-hub.md new file mode 100644 index 00000000..62123ba4 --- /dev/null +++ b/docs/dev/realtime/user-hub.md @@ -0,0 +1,147 @@ + +# User Hub + +**Endpoint:** `https://api.openshock.app/1/hubs/user` + +The user hub is the primary channel for authenticated users to control devices and receive real-time events. + +Connect using any **SignalR client library**, the transport is **WebSocket-only** with **JSON** payloads. + +## Connecting + +Provide the authentication headers described in the [overview](./#authentication), then connect to the endpoint using your SignalR client's WebSocket transport. + +## Server Methods + +Methods your client can invoke on the hub. + +### `ControlV2` + +Send one or more control commands to shockers, allows specifying a custom sender name that appears in control logs. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `shocks` | array of [Control](./types#control-object) | One or more shocker commands. | +| `customName` | string or null | Custom name to display as the sender in logs. | + +### `CaptivePortal` + +Enable or disable the captive portal on a device. Requires device ownership. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `deviceId` | UUID | The device identifier. | +| `enabled` | boolean | `true` to enable, `false` to disable. | + +### `EmergencyStop` + +Immediately stop all activity on a device. Requires device ownership. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `deviceId` | UUID | The device identifier. | + +### `OtaInstall` + +Trigger a firmware update on a device. Requires device ownership. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `deviceId` | UUID | The device identifier. | +| `version` | string | Semantic version to install (e.g. `1.2.0`). | + +### `Reboot` + +Reboot a device. Requires device ownership. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `deviceId` | UUID | The device identifier. | + +## Client Events + +Events the server sends to your client. Register handlers for these using your SignalR client's event subscription mechanism. + +### `Welcome` + +Sent immediately after connecting. + +| Field | Type | Description | +|-------|------|-------------| +| `connectionId` | string | Your SignalR connection ID. | + +### `DeviceStatus` + +Provides the current online state and firmware version for all devices accessible to the user. Sent on connect and whenever a device's status changes. + +| Field | Type | Description | +|-------|------|-------------| +| `deviceOnlineStates` | array of [DeviceOnlineState](./types#deviceonlinestate) | Status of each device. | + +### `Log` + +Emitted when a control command is executed on a device. + +| Field | Type | Description | +|-------|------|-------------| +| `sender` | [ControlLogSender](./types#controllogsender) | Who triggered the command. | +| `logs` | array of [ControlLog](./types#controllog) | Log entries for each shocker affected. | + +### `DeviceUpdate` + +Notifies when a device or its shockers are modified or removed. + +| Field | Type | Description | +|-------|------|-------------| +| `deviceId` | UUID | The device identifier. | +| `type` | [DeviceUpdateType](./types#deviceupdatetype) | What changed. | + +### `OtaInstallStarted` + +A firmware update has begun. + +| Field | Type | Description | +|-------|------|-------------| +| `deviceId` | UUID | The device identifier. | +| `updateId` | integer | Unique update identifier. | +| `version` | string | Version being installed. | + +### `OtaInstallProgress` + +Progress update for an ongoing firmware install. + +| Field | Type | Description | +|-------|------|-------------| +| `deviceId` | UUID | The device identifier. | +| `updateId` | integer | Unique update identifier. | +| `task` | [OtaUpdateProgressTask](./types#otaupdateprogresstask) | Current stage of the update. | +| `progress` | float | Progress within the current task (0.0 -- 1.0). | + +### `OtaInstallFailed` + +Firmware install failed. + +| Field | Type | Description | +|-------|------|-------------| +| `deviceId` | UUID | The device identifier. | +| `updateId` | integer | Unique update identifier. | +| `fatal` | boolean | Whether the failure triggered a rollback. | +| `message` | string | Error description. | + +### `OtaRollback` + +Firmware rolled back to the previous version after a failed update. + +| Field | Type | Description | +|-------|------|-------------| +| `deviceId` | UUID | The device identifier. | +| `updateId` | integer | Unique update identifier. | + +### `OtaInstallSucceeded` + +Firmware update completed successfully. + +| Field | Type | Description | +|-------|------|-------------| +| `deviceId` | UUID | The device identifier. | +| `updateId` | integer | Unique update identifier. | diff --git a/docs/dev/signalr-websockets.md b/docs/dev/signalr-websockets.md new file mode 100644 index 00000000..733c567a --- /dev/null +++ b/docs/dev/signalr-websockets.md @@ -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 shocks)` | Send one or more control commands to shockers. Each command requires `id`, `type`, `intensity`, `duration` and optional `exclusive`. | +| `ControlV2` | `ControlV2(IReadOnlyList 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 deviceOnlineStates)` | Provides online status and firmware version for devices accessible to the user. | +| `Log` | `Log(ControlLogSender sender, IEnumerable 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. + diff --git a/docs/dev/signalr.md b/docs/dev/signalr.md deleted file mode 100644 index 94fe124b..00000000 --- a/docs/dev/signalr.md +++ /dev/null @@ -1,111 +0,0 @@ - -# 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", { - 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 shocks)` | Send one or more control commands to shockers. Each command requires `id`, `type`, `intensity`, `duration` and optional `exclusive`. | -| `ControlV2` | `ControlV2(IReadOnlyList 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 deviceOnlineStates)` | Provides online status and firmware version for devices accessible to the user. | -| `Log` | `Log(ControlLogSender sender, IEnumerable 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. diff --git a/docs/dev/signalr/user.md b/docs/dev/signalr/user.md deleted file mode 100644 index eb4506f3..00000000 --- a/docs/dev/signalr/user.md +++ /dev/null @@ -1,106 +0,0 @@ - -# SignalR Hub: User - -URL: `https://api.openshock.app/1/hubs/user` - -## Endpoint - - -## Connecting - -Use a SignalR client and provide the required headers: - -- `User-Agent`: A meaningful identifier for your application. Browsers do this automatically -- `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", { - transport: HttpTransportType.WebSockets, - skipNegotiation: true, - }) - .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 shocks)` | Send one or more control commands to shockers. Each command requires `id`, `type`, `intensity`, `duration` and optional `exclusive`. | -| `ControlV2` | `ControlV2(IReadOnlyList 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 deviceOnlineStates)` | Provides online status and firmware version for devices accessible to the user. | -| `Log` | `Log(ControlLogSender sender, IEnumerable 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. diff --git a/docs/guides/openshock/first-setup.md b/docs/guides/openshock/first-setup.md index bcdce900..8f3f4c3e 100644 --- a/docs/guides/openshock/first-setup.md +++ b/docs/guides/openshock/first-setup.md @@ -29,9 +29,13 @@ The electricity could flow through your heart. ![Image "image"](../../static/guides/first-setup/WiFioverview.png) ::: 2. Connect to the hub via the network: - 1. Open your phone's browser and type in ``10.10.10.10`` *or* ``openshock.local`` this should open up a web-interface for the hub. - 2. Find your router's Wi-Fi network in the web-interface. - 3. Press the green button next to it and type in your Wi-Fi password, then press submit. + 1. A web page should automatically pop up on your phone. + * If it does, continue with steps 3 and 4 below. + 2. If the page does **not** appear automatically: + * Open your browser and go to `http://4.3.2.1` + * If that doesn’t work, try `http://10.10.10.10` instead + 3. In the web interface, find your router’s Wi-Fi network. + 4. Press the green button next to it, enter your Wi-Fi password, and press **Submit**. *A green pop up should appear if it's connected successfully* ::: details Images (click to expand) ![Image "image"](../../static/guides/first-setup/ESPWebGUI.png) diff --git a/docs/troubleshooting/hub.md b/docs/troubleshooting/hub.md index 028678e7..b3ed2cac 100644 --- a/docs/troubleshooting/hub.md +++ b/docs/troubleshooting/hub.md @@ -24,7 +24,7 @@ The RF TX (SIG) pin is the GPIO pin on your ESP32 that controls the signal/comma Usually you set this pin during the first time setup of your Hub. If you have already done Setup and your Hub is online, there are two ways to change the pin: -1. Re-enable the Captive Portal via the website (`Hubs -> Three dots -> Remote Debug -> Captive Portal On`), then 10.10.10.10 etc. +1. Re-enable the Captive Portal via the website (`Hubs -> Three dots -> Remote Debug -> Captive Portal On`), then 4.3.2.1 etc. 2. Connect a serial terminal to the ESP (via USB UART) and use the `rftxpin` command followed by the number of your GPIO pin. A nice and reliable web serial terminal is available here: https://serial.huhn.me