Skip to main content

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 service (systemd or OpenRC) and:

  1. Auto-detects your HDMI audio card, or uses the one saved in the config file
  2. Monitors D-Bus for PrepareForSleep signals from systemd-logind (or elogind)
  3. 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
  • pactl
  • dbus-monitor
  • A Linux distribution with systemd or OpenRC (elogind for OpenRC)

Installation

Recommended: uv

uv tool install aproman
aproman install-service

The install-service command auto-detects the init system:

  • systemd: installs a user service, enables it
  • OpenRC 0.60+: installs a user init script
  • OpenRC (older): installs a system init script (requires root)

Then start the service:

# systemd
systemctl --user start aproman.service

# OpenRC (user)
rc-service --user aproman start

# OpenRC (system)
rc-service aproman start

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:

# systemd
systemctl --user status aproman.service
journalctl --user -u aproman.service -f

# OpenRC (user)
rc-service --user aproman status

# OpenRC (system)
rc-service aproman status

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 service (systemd or OpenRC)
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 service (systemd or OpenRC)

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 reload signal (sent automatically by set-default-card and set-default-profile via the Unix socket, 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

bun run test                # unit tests
bun run test:integration    # Docker-based integration tests
bun run test:all            # both

Hooks

bun run hooks:check         # run checks against working tree
lefthook install            # install git hooks

The pre-commit hook runs uvx ruff check, uvx ty check, and the unit test suite.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aproman-0.4.0.tar.gz (22.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aproman-0.4.0-py3-none-any.whl (11.9 kB view details)

Uploaded Python 3

File details

Details for the file aproman-0.4.0.tar.gz.

File metadata

  • Download URL: aproman-0.4.0.tar.gz
  • Upload date:
  • Size: 22.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for aproman-0.4.0.tar.gz
Algorithm Hash digest
SHA256 605256942c095687b1a2d8ca693f083e095d329d1c757c4f04ddc1c9a40cf49b
MD5 5540683010a43023ed1d75b644097f67
BLAKE2b-256 8c6b2116811ef086b268bde080349f3a53d70673d4f5e4668bd135e039037d99

See more details on using hashes here.

Provenance

The following attestation bundles were made for aproman-0.4.0.tar.gz:

Publisher: publish.yml on mwolson/aproman

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aproman-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: aproman-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 11.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for aproman-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 22f93af986da0db007ef0fbb754bcccd3de61aaa4b503306d46003310a4d83c7
MD5 ca8715b93f1110bd9e848ccd97f806aa
BLAKE2b-256 069a9b123de3f651ab78ac42c01a89b6c758a5b78f7fa3a5830083ffec279382

See more details on using hashes here.

Provenance

The following attestation bundles were made for aproman-0.4.0-py3-none-any.whl:

Publisher: publish.yml on mwolson/aproman

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page