Skip to content
Everframe Docs
Documentation

Companion mode

For screens with no keyboard. The TV captures; a phone or the dashboard writes the report.

Updated

On a TV there is no keyboard and no screenshot key, so Everframe does not put a reporter on the TV at all. The app runs in companion mode: it shows a pairing code, and the reporter lives on a phone in your hand or in the dashboard on your desk. The TV still captures everything — screenshot, breadcrumbs, console, network, device context — it just does not ask anyone to type.

This is how native Android TV and Fire TV apps report. If your TV app is React Native, use @everframe/react-native instead — it drives this same machinery through the bridge, and calling both would be starting the session twice.

Starting a session

Everframe.start(config)      // the SDK itself, first
Everframe.startCompanion()   // then the companion relay

// …later
Everframe.stopCompanion()

startCompanion() takes no arguments — it reads the SDK key and the companion fields off the config you already passed to start().

Two behaviours worth knowing:

  • It no-ops before start() has run. There is no config to read yet, so the call is silently ignored rather than half-starting a session. Order matters.
  • It no-ops while a session is already live. Call stopCompanion() first to restart.

Reading session state

Everframe.companion exposes each field as a Kotlin StateFlow:

lifecycleScope.launch {
    Everframe.companion.state.collect { state ->
        when (state) {
            CompanionState.Unpaired -> showQr(
                Everframe.companion.pairUrl.value,
                Everframe.companion.code.value,
            )
            else -> hideQr()
        }
    }
}
FlowWhat it is
stateWhat the relay is doing — see the table below.
pairUrlThe URL to render as a QR code. null until the socket is up.
codeShort display code, shown beside the QR so a teammate can find this device in the dashboard’s Companion list.
attachedUserNameThe dashboard user’s display name once someone attaches.
resolvedNameThe server-resolved name for this device.
attachChallengeA live attach-PIN request — AttachChallengeInfo(code, requestedByName, ttlMs).

There is no running flag here — that is a JavaScript-side concept. On Android you already know whether you called startCompanion().

Session states

CompanionStateMeaning
UnpairedNo active pairing. The relay has issued a pair token — show the QR.
PairedA phone is bonded, awaiting a report trigger.
ReportInProgressA report request arrived; the payload is being captured and shipped.
PhoneDisconnectedBackgrounded or the socket dropped. Reconnect with backoff is already scheduled.

Attach from the dashboard

A teammate can attach from the dashboard’s Companion tab instead of scanning. When they do, the TV shows a four-digit PIN that the teammate types to confirm — so attaching to a screen always requires someone who can see it.

The SDK renders that PIN for you by default. A host running AttachPinUi.CUSTOM collects it from Everframe.companion.attachChallenge and renders its own. If you cannot commit to rendering it, choose the off mode rather than CUSTOM — CUSTOM suppresses the built-in dialog, so a host that sets it and renders nothing leaves every attach request stranded until the challenge expires.

Configuration

Three companion fields live on EverframeConfig:

FieldDefaultWhat it does
companionDeviceIdnullStable device identity override. null lets the SDK derive and hash one itself.
companionBadgeEnabledtrueThe on-screen badge naming who is attached.
companionBadgePositionnull"bottom-right" (default), "bottom-left", "top-right" or "top-left".

The badge is identification, not a privacy control. It tells a person in the room who is attached; it is not a capture indicator and must not be described as one.