D14: libmosquitto from Debian, shipped in the package (Qt MQTT needs the exact Qt version; nothing on the Frame). D15: own device-based discovery layer on Qt's JSON classes. Document the libraries per requirement and what the Frame provides (Qt modules, OpenSSL 3.2.1, glibc 2.39). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FrameAssistant
Steam Frame companion for Home Assistant.
FrameAssistant runs on the Steam Frame (and on x86_64 Linux PCs with SteamVR). It connects the headset to Home Assistant in both directions:
- Sensors: it reports the state of the headset and its surroundings to Home Assistant.
- Services: Home Assistant can call it, e.g. to show notifications in the headset.
- Dashboard: a SteamVR dashboard tab to view and control Home Assistant from inside VR.
Status: early development. The overlay platform works on the Frame; the Home Assistant parts are being planned; see docs/REQUIREMENTS.md and docs/PROGRESS.md.
Platform
FrameAssistant grew out of a SteamVR dashboard-overlay template, which it still builds on:
- Plain QML UI: rendered off-screen and shown as a SteamVR dashboard tab. Users can float or pin the tab anywhere, and it stays visible in games.
- Input: SteamVR's laser (as mouse), thumbstick scrolling, the SteamVR keyboard (opens for text fields), and D-pad/button navigation via rebindable SteamVR Input actions.
- Vulkan by default, with OpenGL as an automatic fallback. On the Frame, Vulkan is about 1.9× faster, since OpenGL runs through zink there.
- Several views per app ("surfaces"), e.g. several dashboard tabs sharing one QML engine and its data.
- Develop without a headset: desktop preview window, offscreen snapshots, benchmark.
- Frame packages of about 3 MB, using SteamOS's own Qt; a self-contained variant with Qt
bundled is also available. Packages come with
install.sh/uninstall.shand a deploy script.
Quick start
Requirements:
- CMake ≥ 3.22, Ninja, a C++20 compiler, and Qt ≥ 6.5 (Quick, QuickControls2, OpenGL; Test for the tests)
- Vulkan headers (optional; without them the build is OpenGL only)
- Docker or Podman, for the Frame builds
The OpenVR SDK files are downloaded automatically when CMake configures the build
(-DOPENVR_ROOT=<sdk> to build offline).
cmake --preset native-debug && cmake --build --preset native-debug
ctest --preset native-debug
build/native-debug/bin/frame-assistant --desktop # UI in a window, no SteamVR
build/native-debug/bin/frame-assistant --snapshot out.png # one frame through the VR path
build/native-debug/bin/frame-assistant # as dashboard tab (SteamVR running)
build/native-debug/bin/frame-assistant --install-manifest # register + auto-start with SteamVR
On the Steam Frame:
scripts/package-release.sh # → dist/frame-assistant-0.1.0-linux-arm64.tar.gz
scripts/deploy-to-frame.sh <user>@<frame> # copy over SSH and install (SteamVR must run)
Details are in docs/STEAM-FRAME.md.
Development
- App identity (app key
net.matzman.frameassistant, tab name, executable nameframe-assistant, texture size) is set in the rootCMakeLists.txt. The executable must not be named like a SteamVR process such asvrdashboard(CMake checks this). - UI:
src/qml/, withMain.qmlas the root. List new QML files underQML_FILESinvrdash_add_dashboard()insrc/CMakeLists.txt. Iterate with--desktopand--snapshot. - C++ logic: the app class
src/Dashboard.h/.cppderives fromDashboardApplication. It adds views in its constructor (addSurface()), keeps its objects as members and wires them up in theloading()/started()overrides. - Controller buttons: actions and default bindings are in
resources/input/. - Ship it:
scripts/package-release.shandscripts/deploy-to-frame.sh, see docs/STEAM-FRAME.md.
How the pieces fit together: docs/ARCHITECTURE.md.
Command line
frame-assistant understands:
| Option | Meaning |
|---|---|
| (none) | Run as SteamVR dashboard overlay (SteamVR must be running) |
--desktop |
Show the UI in desktop windows, one per surface; no SteamVR |
--snapshot <png> |
Render one frame per surface off-screen and save it (<name>-<id>.png with several surfaces) |
--benchmark <frames> |
Render frames back to back with a full GPU wait each and print frame times |
--graphics-api vulkan|opengl |
Render backend. Default: Vulkan, falling back to OpenGL if Vulkan doesn't start; an explicit choice never falls back |
--app-key <key>, --app-name <name> |
Override app key and name, e.g. to run a second instance |
--max-fps <fps> |
Frame-rate cap (default 60); rendering only happens on change |
--install-manifest [--launch] |
Register with SteamVR (auto-start) and optionally start it now; works over SSH |
--uninstall-manifest |
Unregister |
--manifest <path> |
Manifest for the two options above (default: the app's own) |
--log-file <path> |
Also log to a file (relative paths: relative to the install folder) |
Testing
ctest --preset native-debug # unit + render tests (GPU tests skip without a GPU context)
scripts/container-test.sh # all tests in Debian incl. Vulkan with validation layers
VK_DRIVER=lvp scripts/container-test.sh # same on the lavapipe software driver
Repository layout
| Path | Content |
|---|---|
src/main.cpp, src/Dashboard.*, src/qml/ |
The app |
src/app/ |
DashboardApplication (app base class), Surface (one view) |
src/render/ |
Off-screen Qt Quick renderer; Vulkan and OpenGL backends |
src/vr/ |
SteamVR connection, overlays (dashboard tab) |
src/input/ |
Laser/scroll/keyboard → Qt events; SteamVR Input navigation |
resources/ |
SteamVR manifest template, input actions and bindings |
tests/ |
Unit and render tests |
cmake/ |
vrdash_add_dashboard(), OpenVR download, Qt bundling, toolchain |
docker/, scripts/ |
Build containers; build, package, deploy and test scripts |
docs/ |
Requirements, progress, architecture, Steam Frame notes |
License
MIT, see LICENSE. Third-party components (OpenVR SDK, Qt; in bundled builds also their dependencies) are listed in THIRD_PARTY_NOTICES.md.