Surface Plugin Overview
A surface module exports a single object that implements the SurfacePlugin interface from
@companion-surface/base. Where a connection module exposes a class that Companion instantiates
once per connection, a surface plugin is one long-lived object that manages every device of
the type it supports.
One plugin handles all surfaces of a given type. The plugin discovers and opens devices; each opened device is then represented by its own surface instance.
The interface is generic over TInfo — a type you define to carry whatever plugin-specific data
you want to associate with a discovered device (a device path, an IP address, a handle into a
vendor library, …). Companion hands it back to you when it asks you to open that device.
A minimal plugin looks like this:
import type { SurfacePlugin } from '@companion-surface/base'
interface MyDeviceInfo {
path: string
}
export const plugin: SurfacePlugin<MyDeviceInfo> = {
async init() {
// Set up anything the plugin needs. Don't block on hardware that may not be present yet.
},
async destroy() {
// Tear down anything created in init().
},
// Tell Companion which connected devices are yours — see Discovery.
checkSupportsHidDevice(device) {
return null
},
// Open a device Companion has chosen to use — see The Surface Instance.
async openSurface(surfaceId, pluginInfo, context) {
throw new Error('not implemented')
},
}
See the generated API reference for the full, always-current interface and its types.
The two halves of a plugin
- Discovery — telling Companion which devices exist. The flow depends on how your hardware connects; the common ones (USB HID and remote/network) are covered in Discovery, the rest in Custom Discovery.
- The surface instance — once Companion decides to use a device it calls
openSurface(), which returns the live surface instance plus a description of the device's layout, brightness support, pincode handling and config fields.
SurfaceContext
openSurface() is given a SurfaceContext. This is your channel back to Companion: you use it
to report input events (key presses, encoder rotation, …), to disconnect when the
device goes away, and to read host state such as whether the surface is locked. Hold onto it inside
your surface instance.
The manifest
Surface manifests look like connection manifests but have "type": "surface" and some
surface-specific fields — most importantly usbIds, which determines the HID devices you are
offered (covered on the Discovery page). For the fields shared with connection
modules, see the connection manifest docs.