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
This commit is contained in:
Stefan Agner 2023-10-23 21:57:57 +02:00 committed by GitHub
parent 7c576da32c
commit a390500309
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 28 additions and 4 deletions

View File

@ -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)

View File

@ -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