Follow up PR #1 with event-driven logind sleep/resume handling, portable wake pipes, error and shutdown priority, and startup cleanup. Preserve the contributor’s polling optimization and include gesture regressions, private-bus resume smoke testing, and repeatable Frame CPU measurements. The final tree matches the implementation already installed and tested on the Frame. All 76 tests passed on Mac and Frame; virtual input and resume checks passed, and the user confirmed physical announcements and sleep/wake.
11 KiB
Steam Frame HEV battery announcements
Double-tap your Steam Frame's power button to hear “Power level is … percent” in the original Half-Life HEV suit voice.
- Double tap while awake: reads the exact battery percentage.
- Single press: puts the Frame to sleep, with a short 350 ms delay.
- Hold: opens Steam's normal power menu.
Runs in the background and starts with your Steam session. No root access, additional packages, or SteamOS filesystem unlock needed.
Install
FrameDrop
Get FrameDrop · Download the Linux ARM64 package
- Enable Settings → System → Enable Developer Mode on the Frame. Keep it awake and on the same Wi-Fi as your PC.
- Pair once: on the headset choose Settings → Developer → Pair new host; in FrameDrop click Pair, then approve on the headset.
- Click Install with FrameDrop above and confirm the installation in FrameDrop. You can also drop the Linux ARM64 ZIP into FrameDrop.
- On the headset, open Library → Non-Steam → Frame HEV and choose Install. Wait for the success message, then close the app.
Setup downloads the original voice clips, so the Frame needs internet access the first time. Announcements then run in the background; you do not need to keep the library entry open. Launch it again to update or uninstall. Removing only the Steam library entry does not uninstall the background service.
The button requires HEV v0.1.2 or later. With v0.1.1, download the Linux ARM64 ZIP in your browser and drop the saved file into FrameDrop. FrameDrop 1.0.37 loses the ZIP extension when following GitHub's download redirect; v0.1.2 uses a self-contained Linux installer that survives this rename. See package and validation details.
Terminal installation
1. Prepare the Frame once
On the headset, enable Settings → System → Enable Developer Mode, then open
Settings → Developer → Set User Password and choose a password. This enables
SSH access as steamos; use that password when the installer asks.
See Valve's setup guide
and SSH documentation.
Keep the Frame awake, with Steam running, and connect it to the same network as your computer.
2. Download and run
Download the release ZIP, or choose Code → Download ZIP on GitHub, and extract the whole folder. Open a terminal in that extracted folder.
macOS or Linux:
bash install.sh
Windows PowerShell:
Use a PowerShell tab in Windows Terminal, or press Win+R, type
powershell, and press Enter to open a normal console. PowerShell ISE cannot
handle SSH's interactive keyboard prompts. Run the command from the extracted
folder without piping or redirecting its input.
powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1
Enter the Frame's password when SSH asks. Password characters do not appear as you type. Windows may ask twice, once to upload and once to install. On the first connection, SSH may ask you to accept the headset's host key.
The installer copies the app to the Frame, downloads and verifies the original audio, and enables the background service. Your computer needs SSH and tar; Windows also needs scp (part of OpenSSH Client). Python is only required on the Frame, where SteamOS already provides it.
When it says installed, double-tap power on the awake headset. Installation does not automatically play a sound.
If frame.local cannot be found, try frame or the Frame's IP address:
bash install.sh --host steamos@frame
powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -HostName steamos@frame
Already using a terminal on the Frame? Extract the download there and run
bash install.sh --local. All installer commands run as the normal Steam user,
without sudo.
Update or uninstall
To update, download a newer release and run the same installer again. It reuses verified audio and preserves your settings. It prepares and checks the update before replacing the running installation, and rolls back if startup fails.
With FrameDrop, install the newer package and launch Frame HEV again, then choose Install / Update. Choose Uninstall in that window to remove the background service while keeping your settings and audio cache.
To uninstall, connect with ssh steamos@frame.local and run:
~/.local/bin/frame-hev uninstall --purge
This removes the app, startup service, settings, and cached audio. Omit
--purge to keep your settings and cache. The native power-button handler
receives presses again when the HEV service stops.
Controls and settings
These commands run on the Frame, either in its terminal or over SSH:
~/.local/bin/frame-hev announce # Speak the current battery
~/.local/bin/frame-hev status # Check the service
~/.local/bin/frame-hev logs # Follow logs; Ctrl+C to exit
~/.local/bin/frame-hev stop # Temporarily restore normal power behavior
~/.local/bin/frame-hev start
Edit ~/.config/frame-hev/environment, then run
~/.local/bin/frame-hev restart.
| Setting | Default | Meaning |
|---|---|---|
FRAME_HEV_VOLUME |
0.65 |
Announcement volume from 0.0 to 1.0, relative to headset volume |
FRAME_HEV_DOUBLE_TAP_MS |
350 |
Maximum gap between the first release and second press |
FRAME_HEV_BUTTON |
power |
Use aux for the button above power instead |
Aux mode also allows the button's normal action to occur. Power mode is the tested default. After waking the Frame, wait one second before double-tapping; the wake press is ignored so it does not immediately put the headset to sleep.
At 0%, the original “Armor compromised” clip plays, because the HEV vocabulary does not contain a recorded zero.
Announcements use SteamVR's headset battery percentage to match Steam's
display. The kernel's raw battery reading can differ by several points. If
SteamVR cannot provide a valid reading, HEV uses the kernel value and logs that
fallback; frame-hev doctor shows the selected source and the raw reading.
Audio
The source repository and release downloads contain no Half-Life audio. The installer fetches the original WAV clips from hl1sfx.com and verifies their recorded SHA-256 hashes and WAV format. Audio remains Valve's property and is not covered by the code's license.
To use loose audio files from your own Half-Life installation, point to its
valve, sound, or fvox directory on the Frame:
bash install.sh --source-dir /path/on/frame/to/Half-Life/valve
On Windows, use -SourceDir instead. --offline (Windows: -Offline) prevents
downloads and uses verified files already on the Frame. See
audio provenance and import details.
Troubleshooting
-
Cannot connect: keep the headset awake, check Developer Mode and your password, and try
ssh steamos@frame.local. Tryframeor the headset's IP if your network does not resolve.localnames. -
Windows prompt accepts no typing: use Windows Terminal or a normal PowerShell console, rather than PowerShell ISE. Run
ssh steamos@frame.localthere first. At the host-key confirmation, typeyesand press Enter after checking it is your Frame; at the password prompt, characters are invisible. After connecting, typeexit, then rerun the installer from the extracted folder. If even standalone SSH cannot accept input, the issue is with the terminal/SSH client before the Frame installer starts. -
“Wake the Frame” while it is already awake (v0.1.0): that release uses the same error for an inactive native button service and a failed connection to the Steam user's service manager. It does not prove the headset is asleep. A desktop terminal can also have different session environment variables. From the extracted folder on the Frame, retry with the Steam user's session:
env XDG_RUNTIME_DIR="/run/user/$(id -u)" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus" bash install.sh --localIf it still fails, leave Steam/SteamVR running and collect the actual state with this read-only command on the Frame:
env XDG_RUNTIME_DIR="/run/user/$(id -u)" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus" systemctl --user --no-pager --full status steamos-powerbuttond.service steamvr.serviceInclude that output and your SteamOS version in a bug report. The current installer selects this session automatically and reports the specific failure. Keep the native power-button service installed and enabled.
-
No sound: check headset volume, run
~/.local/bin/frame-hev announce, then~/.local/bin/frame-hev doctorand~/.local/bin/frame-hev logs. -
Download or checksum error: retry. A failed download does not replace a working installation. If the source remains unavailable, use a local game copy as described above.
-
After a SteamOS update: rerun the installer. Input or Steam interfaces may change across firmware versions.
Development and releases
Tested on SteamOS 0.4.3, variant vr, build 20260930.6234839. Physical double-tap
announcements and single-press sleep/wake were confirmed. The Windows installer
is provided, but has not yet been run end-to-end on Windows.
python3 -m unittest discover -s tests -v
python3 tools/build_release.py
On the Frame, python3 tools/smoke_input.py checks virtual button delivery and
release of the input grab. python3 tools/smoke_input.py --resume checks aux
resume handling through an isolated D-Bus, without suspending the headset.
python3 tools/measure_idle.py --seconds 60 measures idle CPU time and context
switches on a virtual button. These checks do not generate physical power events.
The release builder creates the source ZIP, tar.gz, and dist/SHA256SUMS, using
the version in VERSION. To also build the FrameDrop ZIP and manifest, pass
--framedrop-launcher /path/to/frame-hev-setup; see
FrameDrop release instructions. The builder uses an
explicit file list and excludes audio, credentials, local agent settings, and
Git history.
Frame development notes include remote UI inspection, screenshots, and click automation. Validation details separate automated results from physical checks.
License
Application code: GPL-3.0-only. See LICENSE. Half-Life audio is separate Valve material. This is an unofficial community project and is not affiliated with or endorsed by Valve.