From a390500309cce52b90aa37efa6aeadef9d06b455 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 23 Oct 2023 21:57:57 +0200 Subject: [PATCH] Reload Pulseaudio modules on hardware change (#4638) * Reload Pulseaudio modules on hardware change In the past the audio plug-in restarted Pulseaudio on hardware change. This broke with the s6 updates. However, it also turns out that this is quite racy: The Supervisor reloads audio data much too quickly, when Supervisor isn't restarted yet. Instead, let's reload the relevant modules from Supervisor itself. This works well with a USB microphone on Home Assistant Green. Related change: https://github.com/home-assistant/plugin-audio/pull/153 * Fix linter issue --- supervisor/host/manager.py | 2 +- supervisor/host/sound.py | 30 +++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/supervisor/host/manager.py b/supervisor/host/manager.py index af7efce23..a9d858e96 100644 --- a/supervisor/host/manager.py +++ b/supervisor/host/manager.py @@ -175,4 +175,4 @@ class HostManager(CoreSysAttributes): async def _hardware_events(self, device: Device) -> None: """Process hardware requests.""" if self.sys_hardware.policy.is_match_cgroup(PolicyGroup.AUDIO, device): - await self.sound.update() + await self.sound.update(reload_pulse=True) diff --git a/supervisor/host/sound.py b/supervisor/host/sound.py index 8413c349c..4f64e952a 100644 --- a/supervisor/host/sound.py +++ b/supervisor/host/sound.py @@ -15,6 +15,9 @@ _LOGGER: logging.Logger = logging.getLogger(__name__) PULSE_NAME = "supervisor" +PULSE_ALSA_MODULE = "module-alsa-card" +PULSE_UDEV_MODULE = "module-udev-detect" + class StreamType(StrEnum): """INPUT/OUTPUT type of source.""" @@ -235,9 +238,9 @@ class SoundControl(CoreSysAttributes): @Job( name="sound_control_update", limit=JobExecutionLimit.THROTTLE_WAIT, - throttle_period=timedelta(seconds=10), + throttle_period=timedelta(seconds=2), ) - async def update(self): + async def update(self, reload_pulse: bool = False): """Update properties over dbus.""" _LOGGER.info("Updating PulseAudio information") @@ -348,11 +351,32 @@ class SoundControl(CoreSysAttributes): f"Error while processing pulse update: {err}", _LOGGER.error ) from err except PulseError as err: - _LOGGER.debug("Can't update PulseAudio data: %s", err) + _LOGGER.warning("Can't update PulseAudio data: %s", err) return data + def _reload_pulse_modules(): + try: + with Pulse(PULSE_NAME) as pulse: + modules = pulse.module_list() + for alsa_module in filter( + lambda x: x.name == PULSE_ALSA_MODULE, modules + ): + pulse.module_unload(alsa_module.index) + udev_module = next( + filter(lambda x: x.name == PULSE_UDEV_MODULE, modules) + ) + pulse.module_unload(udev_module.index) + # And now reload + pulse.module_load(PULSE_UDEV_MODULE) + except StopIteration: + _LOGGER.warning("Can't reload PulseAudio modules.") + except PulseError as err: + _LOGGER.warning("Can't reload PulseAudio modules: %s", err) + # Update data from pulse server + if reload_pulse: + await self.sys_run_in_executor(_reload_pulse_modules) data: PulseData = await self.sys_run_in_executor(_get_pulse_data) self._applications = data.applications self._cards = data.cards