Skip to main content

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. The profile cycle alone is usually enough to force WirePlumber to rebuild fresh nodes and restore audio.
  4. Separately, monitors PipeWire via pw-dump for nodes entering an error state. If one is detected, restarts PipeWire to recover (with a 30-second cooldown to prevent restart loops). This reactive path is a safety net for cases where the profile cycle alone is not enough; it is deliberately kept off the happy resume path because restarting PipeWire churns every client connected to it, and some clients (e.g. quickshell-based shells) handle the churn poorly.

Requirements

  • PipeWire with WirePlumber, or PulseAudio compatibility via PipeWire
  • pactl
  • dbus-monitor
  • pw-dump (optional, for node error monitoring)
  • A Linux distribution with systemd or OpenRC (elogind for OpenRC)

Installation

systemd

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.

OpenRC user service (0.60+, Alpine edge, etc.)

uv tool install aproman
aproman install-service
rc-service --user aproman start

On OpenRC 0.60 or newer, install-service automatically installs a user-level service to ~/.config/rc/init.d/aproman. Make sure ~/.local/bin is on your PATH.

OpenRC system service (older OpenRC)

sudo uv pip install --system --break-system-packages aproman
sudo aproman install-service
sudo rc-service aproman start

On OpenRC versions before 0.60, install-service installs a system-level init script to /etc/init.d/aproman and adds it to the default runlevel. The service uses supervise-daemon for process supervision with automatic restart.

To configure the user and environment for the daemon, create /etc/conf.d/aproman:

command_user="youruser"
supervise_daemon_args="--env XDG_RUNTIME_DIR=/run/user/1000"

Replace 1000 with your user's UID (id -u youruser).

Alternative: install.sh (systemd)

git clone https://github.com/mwolson/aproman-py.git
cd aproman-py
./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, 0.60+)

rc-service --user aproman status

OpenRC (system, older)

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

Release files for aproman 0.5.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aproman 0.5.5
File Size Uploaded
aproman-0.5.5.tar.gz 25.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aproman 0.5.5
File Interpreter ABI Platform
aproman-0.5.5-py3-none-any.whl Python 3 none any Details

Total release size: 38.8 kB

Release files / aproman-0.5.5.tar.gz

Download URL aproman-0.5.5.tar.gz
Size 25.5 kB
Tags Source
SHA-256 checksum
How to use checksums
4d01eaf5900f07c8d2a1e1d6f24f327ca0b34d515c9a40d4e297ce0e5be76659
BLAKE2b-256 checksum
How to use checksums
503d1ff67a17b16fe90d0e21a838fcb1a6195d52034cef1784e1b841710ac49d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 10, 2026.

Transparency log

Release files / aproman-0.5.5-py3-none-any.whl

Download URL aproman-0.5.5-py3-none-any.whl
Size 13.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bcfe413eb814894dfea0642bc5d8a76052ec2be4785a27ee95543e5a1832834b
BLAKE2b-256 checksum
How to use checksums
2420b3ec087b79ce0183d65a2e52fbb25eb40d4967adbcb41678fa6e124fd07c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.5 This release

2 release files

0.5.4

2 release files

0.5.3

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page