Fix HDMI audio after suspend/resume on PipeWire + WirePlumber
Project description
aproman
Fix HDMI audio after suspend/resume on Linux systems running PipeWire + WirePlumber.
The Problem
When a Linux system resumes from suspend, HDMI audio devices often lose their
connection. WirePlumber tries to link to stale node proxies, resulting in
silence. The only manual fix is to open your audio settings and switch the card
profile away (for example to off) and back, forcing a full teardown and
rebuild of the audio nodes.
How It Works
aproman runs as a user systemd service and:
- Auto-detects your HDMI audio card, or uses the one saved in the config file
- Monitors D-Bus for
PrepareForSleepsignals from systemd-logind - On wake, waits briefly for HDMI to renegotiate, then cycles the card profile off and back on
This forces PipeWire and WirePlumber to rebuild fresh nodes, restoring audio without manual intervention.
Requirements
- PipeWire with WirePlumber, or PulseAudio compatibility via PipeWire
pactldbus-monitorsystemctl- A systemd-based Linux distribution
Installation
Recommended: uv
uv tool install aproman
aproman install-service
systemctl --user start aproman.service
This installs aproman to ~/.local/bin/, copies the systemd user service into
place, and enables it.
Alternative: install.sh
git clone https://github.com/mwolson/aproman.git
cd aproman
./install.sh
systemctl --user start aproman.service
This copies aproman to ~/.local/bin/ and installs and enables the user
service.
Optional: set defaults
After installing, you can optionally save your preferred card and profile so that aproman uses them instead of auto-detecting:
aproman list-cards
aproman set-default-card alsa_card.pci-0000_01_00.1
aproman list-profiles
aproman set-default-profile pro-audio
These write to ~/.config/aproman.conf and signal the running daemon to pick up
the changes. Without defaults, aproman auto-detects the first HDMI card and uses
its active profile at startup.
Usage
The service runs automatically. To check status:
systemctl --user status aproman.service
journalctl --user -u aproman.service -f
Commands
aproman uses subcommands for one-off operations. With no subcommand, it runs as a daemon.
aproman Run as a daemon (default)
aproman cycle Cycle the card profile off and back on
aproman get-default-card Print the default card from the config file
aproman get-default-profile Print the default profile from the config file
aproman install-service Install and enable the systemd user service
aproman list-cards List available audio cards
aproman list-profiles List available profiles for the card
aproman set-default-card CARD Save default card and signal the daemon
aproman set-default-profile PROFILE Save default profile and signal the daemon
aproman uninstall-service Disable and remove the systemd user service
Daemon options
These flags apply to the daemon and to cycle:
--card CARD PipeWire/PulseAudio card name (default: config file, then auto-detect HDMI)
--profile PROFILE Desired audio profile (default: config file, then active profile)
--wake-delay SECONDS Seconds to wait after wake before cycling (default: 3.0)
Configuration File
aproman reads defaults from ~/.config/aproman.conf (or
$XDG_CONFIG_HOME/aproman.conf). The file uses one flag per line:
--card=alsa_card.pci-0000_01_00.1
--profile=pro-audio
Only --card and --profile are supported. Unrecognized flags cause an error
at startup. Command-line arguments always take precedence over the config file.
When the daemon receives a SIGHUP (sent automatically by set-default-card and
set-default-profile, or manually via kill -HUP), it reloads the config file
and updates the card and profile for future suspend/resume cycles.
One-Shot Fix
If audio breaks and the daemon missed the resume event (for example, after a service restart), you can manually trigger a single profile cycle:
aproman cycle
This sends a cycle request to the running daemon via its Unix socket. If the daemon is unavailable, it falls back to running the cycle directly.
When the card is stuck in the off state, cycle automatically selects the
highest-priority available profile. You can override with --profile:
aproman --profile pro-audio cycle
Example: Custom Card and Profile
aproman set-default-card alsa_card.pci-0000_01_00.1
aproman set-default-profile output:hdmi-stereo
Uninstall
aproman uninstall-service
uv tool uninstall aproman # or: rm ~/.local/bin/aproman
rm -f ~/.config/aproman.conf
Testing
python3 -m unittest discover -s tests -v
Hooks
lefthook install
lefthook run pre-commit --all-files
The pre-commit hook runs uvx ruff check, uvx ty check, and the unit test
suite.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aproman-0.3.0.tar.gz.
File metadata
- Download URL: aproman-0.3.0.tar.gz
- Upload date:
- Size: 16.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1507aa829e2e17e5839ff31ba7a80381f56a2d6871c6de6a542c5d598a441aa5
|
|
| MD5 |
6199ecdbfa6b0296eb809f5b0b66ed3f
|
|
| BLAKE2b-256 |
8cde2bc988afa1af510933681a8167fcb35abc1a7a85b31aec922107630752a9
|
Provenance
The following attestation bundles were made for aproman-0.3.0.tar.gz:
Publisher:
publish.yml on mwolson/aproman
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aproman-0.3.0.tar.gz -
Subject digest:
1507aa829e2e17e5839ff31ba7a80381f56a2d6871c6de6a542c5d598a441aa5 - Sigstore transparency entry: 1221959703
- Sigstore integration time:
-
Permalink:
mwolson/aproman@cf6c559bffa6023c21e588044f01fb2f58d07b46 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/mwolson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cf6c559bffa6023c21e588044f01fb2f58d07b46 -
Trigger Event:
push
-
Statement type:
File details
Details for the file aproman-0.3.0-py3-none-any.whl.
File metadata
- Download URL: aproman-0.3.0-py3-none-any.whl
- Upload date:
- Size: 10.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
467a27e0730fb56b8574d5ebd67195951730646cb6461d18b2c7798b40110a06
|
|
| MD5 |
8b406742964d33ea3dcffa7431124e41
|
|
| BLAKE2b-256 |
a61b2dc31a42cabedaad759744d4548978ee1328ed8c969f80fef0dd95b2a4f2
|
Provenance
The following attestation bundles were made for aproman-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on mwolson/aproman
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aproman-0.3.0-py3-none-any.whl -
Subject digest:
467a27e0730fb56b8574d5ebd67195951730646cb6461d18b2c7798b40110a06 - Sigstore transparency entry: 1221959745
- Sigstore integration time:
-
Permalink:
mwolson/aproman@cf6c559bffa6023c21e588044f01fb2f58d07b46 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/mwolson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cf6c559bffa6023c21e588044f01fb2f58d07b46 -
Trigger Event:
push
-
Statement type: