package-release.sh refuses a package whose ELF files need newer glibc or OpenSSL symbol versions than the Frame has (D35); only the version needs section counts. The Qt bundler resolved libmosquitto's dependencies and shipped Debian's OpenSSL 3.5; libssl/libcrypto are now excluded, since SteamOS provides OpenSSL 3. Phase 3 of iteration 2 is done; PROGRESS.md has the manual checklist for phase 4. 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 built: so far the settings file and the MQTT connection (the device appears in Home Assistant with a test sensor, "Uptime"; the tab shows the connection status). See docs/REQUIREMENTS.md, docs/DESIGN.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 windows, no SteamVR
build/native-debug/bin/frame-assistant --desktop-novr # same; never uses 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
Overlay and both desktop modes need a settings file (below) and connect to the MQTT broker;
--snapshot and --benchmark don't.
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.
Settings
FrameAssistant reads its settings from ~/.config/frameassistant/config.json
($XDG_CONFIG_HOME is respected; --config <path> picks another file). Start from
resources/config.example.json, which packages ship as
share/frameassistant/config.example.json:
{
"device": { "name": "Steam Frame" },
"mqtt": { "host": "homeassistant.local", "username": "frameassistant", "password": "…" },
"homeassistant": { "url": "http://homeassistant.local:8123" }
}
| Key | Required | Default | Meaning |
|---|---|---|---|
device.name |
yes | Name of this headset in Home Assistant | |
device.ha_unique_id |
no | derived from the name | Fixed id, so the name can change without creating a new device |
mqtt.host |
yes | MQTT broker | |
mqtt.port |
no | 1883, 8883 with TLS | |
mqtt.username, mqtt.password |
no | no login | |
mqtt.tls.enabled |
no | false |
|
mqtt.tls.ca_file |
no | system certificates | CA of the broker's certificate |
mqtt.tls.cert_file, mqtt.tls.key_file |
no | Client certificate (needs ca_file) |
|
homeassistant.discovery_prefix |
no | homeassistant |
|
homeassistant.url |
no | Home Assistant's address; reserved for the dashboard, not used yet | |
notifications.timeout_s |
no | 10 | How long notifications stay |
Invalid or missing settings stop the app with an error message and exit code 1. Unknown keys
only produce a warning. Keep the file private (chmod 600) when it holds a password.
Development
- Host build dependencies: Qt 6 (≥ 6.5) and libmosquitto with its pkg-config file
(Arch:
mosquitto, Debian:libmosquitto-dev); themosquittobroker for the MQTT integration tests. The containers (docker/) have everything. - 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()) and its own command-line options, keeps its objects as members, loads the settings inprepare()and wires things up in theloading()/started()overrides. Logic without SteamVR or rendering goes into the static libraryframeassistant_lib(e.g.src/config/), which the tests link. - 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 (from iteration 3: uses SteamVR if it runs) |
--desktop-novr |
Like --desktop, but never connects to SteamVR (e.g. on a PC, with its own --config) |
--config <path> |
Settings file (default ~/.config/frameassistant/config.json) |
--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 |
--ha-remove |
Remove this device from Home Assistant (clears its retained MQTT topics; needs the settings and the broker) and exit; uninstall.sh calls it. Works over SSH |
--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, MQTT and render tests (GPU tests skip without a GPU
# context, MQTT tests without the mosquitto broker)
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/config/, src/mqtt/, src/ha/ |
Settings file, MQTT client, HA device and entities (frameassistant_lib) |
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, example settings |
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, design, 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.