# Virtual Controller Integration

> Enable accessible gameplay through Cephable's personal assistant adaptive controls using the Cephable Device Hub with samples in C#/.NET, JavaScript, and C++.

Source: https://developers.cephable.com/docs/game-integrations/virtual-controller

---
## Overview

The **Cephable Virtual Controller** lets players use Cephable's personal assistant adaptive controls — face expressions, head movement, voice commands, and custom switches — to play your game. All input processing runs on-device inside the Cephable app; your game receives only the resulting command strings via a SignalR hub. This makes your game accessible to players who can't use a standard controller without requiring any changes to your core input handling.

There are samples available in **C#/.NET**, **JavaScript (Browser)**, **Android (Kotlin)**, and **iOS (Swift)**.

**Sample repository:** [github.com/Cephable/Cephable-VirtualController-Sample](https://github.com/Cephable/Cephable-VirtualController-Sample)

## How it works

```
Cephable Mobile App (face expressions, voice, gestures, virtual buttons)
      │
      │  (Internet)
      ▼
Cephable Device Hub  (SignalR at https://services.cephable.com/device)
      │
      │  SignalR connection (device token)
      ▼
 Your Game / Application
```

1. The user authenticates with Cephable via OAuth
2. Your app creates a **User Device** on behalf of the user via the Cephable API
3. Your app creates a **Device Token** for that device
4. Your app connects to the Cephable Device Hub with the device token
5. The Cephable app sends commands (face expressions, voice, gestures, virtual buttons) — your app receives them via the `DeviceCommand` event

## Prerequisites

- Start a **free 30-day trial** at [services.cephable.com/trial/developers](https://services.cephable.com/trial/developers) to receive your **OAuth Client ID**, **Client Secret**, and **Device Type ID** — no credit card required

## C# / .NET Integration

The .NET sample uses `Microsoft.AspNetCore.SignalR.Client`:

```xml
<!-- .csproj -->
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
```

### Minimal C# example (from sample)

> This is the complete working sample from [Cephable-VirtualController-Sample/src/dotnet](https://github.com/Cephable/Cephable-VirtualController-Sample/tree/main/src/dotnet):

```csharp
using Microsoft.AspNetCore.SignalR.Client;

Console.WriteLine("Enter your device token (get this from the Cephable API at https://services.cephable.com/swagger): ");
string deviceToken = Console.ReadLine();

var connection = new HubConnectionBuilder()
    .WithUrl("https://services.cephable.com/device", options =>
    {
        options.AccessTokenProvider = () => Task.FromResult(deviceToken);
        options.Headers.Add("X-Device-Token", deviceToken);
    })
    .Build();

connection.On<string>("DeviceCommand", (command) =>
{
    Console.WriteLine("Received command: " + command);
});

await connection.StartAsync();

// Identify the device to the hub
await connection.InvokeAsync("VerifySelf");

Console.WriteLine("Connected to hub. Waiting for commands...");
Console.WriteLine("Press enter to exit.");
Console.ReadLine();
```

> **Getting a device token:** Use the [Swagger UI](https://services.cephable.com/swagger) to create a user device and generate a token, or use the browser sample to walk through the full OAuth + device creation flow.

## JavaScript / Browser Integration

The browser sample uses the SignalR CDN and axios. Run `npm run start` from the `browser` folder to start a local server at `http://localhost:3000`:

```javascript
// From Cephable-VirtualController-Sample/src/browser/index.html
const apiBaseUrl = "https://services.cephable.com/";

// Step 1: Redirect user to Cephable login
const signIn = async () => {
  const clientId = document.getElementById("client_id_field").value;
  localStorage.setItem("clientId", clientId);
  const redirectUri = window.location.origin + window.location.pathname;
  const authUrl = `${apiBaseUrl}signin?redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${clientId}`;
  window.location.href = authUrl;
};

// Step 2: Exchange auth code for access token
const getAccessToken = async (code) => {
  const redirectUri = window.location.origin + window.location.pathname;
  const response = await axios.post(
    `${apiBaseUrl}signin/token?client_id=${clientId}&code=${encodeURIComponent(code)}&redirect_uri=${encodeURIComponent(redirectUri)}&grant_type=code`
  );
  return response.data.access_token;
};

// Step 3: Create a virtual user device
const createUserDevice = async (deviceTypeId) => {
  const response = await axios.post(
    `${apiBaseUrl}api/Device/userDevices/new/${deviceTypeId}?name=Web-Sample`
  );
  return response.data;
};

// Step 4: Create a device token for the hub connection
const createUserDeviceToken = async (userDeviceId) => {
  const response = await axios.post(
    `${apiBaseUrl}api/Device/userDevices/${userDeviceId}/tokens`
  );
  return response.data.token;
};

// Step 5: Connect to the device hub and listen for commands
const startSignalRConnection = async () => {
  const connection = new signalR.HubConnectionBuilder()
    .withUrl(`${apiBaseUrl}device`, {
      accessTokenFactory: () => deviceToken,
      headers: { "X-Device-Token": deviceToken },
    })
    .withAutomaticReconnect()
    .build();

  connection.on("DeviceCommand", function (command, macro) {
    console.log("Received command: " + command);
    console.log("Received macro: " + JSON.stringify(macro));
  });

  await connection.start({ withCredentials: false });
  // Identify device to hub
  const device = await connection.invoke("VerifySelf");
  console.log("Connected. Device:", device);
};
```

## C++ Integration

The C++ demo does an end-to-end flow: OAuth via a local localhost callback, device registration, and SignalR connection using `signalr-client-cpp`.

**Dependencies (install with vcpkg):**
- `cpprest` — for the localhost auth callback server
- `signalr` (aspnet/SignalR-Client-Cpp) — for connecting to the device hub

See the [C++ sample source](https://github.com/Cephable/Cephable-VirtualController-Sample/tree/main/src/c++) for the full implementation.

## Hub events reference

### Incoming (hub → your app)

| Event | Payload | Description |
|-------|---------|-------------|
| `DeviceCommand` | `(command: string, macro: object)` | User triggered an input |
| `DeviceProfileUpdate` | `(device: UserDevice)` | User's profile changed |
| `DeviceSettingsUpdate` | — | Device settings changed |
| `DeviceType` | — | Device type information |
| `StartListening` | — | Hub requests device start listening |
| `StopListening` | — | Hub requests device stop listening |

### Outgoing (your app → hub)

| Method | Description |
|--------|-------------|
| `VerifySelf` | Returns current `UserDevice` state |
| `GetLatestDevice` | Fetches latest device state |
| `DeviceStartedListening` | Notifies hub device is ready |
| `DeviceStoppedListening` | Notifies hub device stopped |

## Macro event types

Commands arrive with an associated macro payload. Macro event types include:

```json
["KeyPress", "Pause", "Type", "MouseMove", "JoysticksMove", "PlayAudio",
 "KeyRelease", "StopOutputs", "KeyToggle", "DeviceTypeCustomAction"]
```

For sample macro payloads and key/button values, see the [sample-data folder](https://github.com/Cephable/Cephable-VirtualController-Sample/tree/main/src/sample-data).

## Next steps

- [Unity Integration](/docs/game-integrations/unity)
- [API Reference](/docs/api-reference/overview)
- [Swagger UI](https://services.cephable.com/swagger) — Explore the Device API
- [Full sample repository](https://github.com/Cephable/Cephable-VirtualController-Sample)
