Prevent using pulseaudio on event loop (#1536)

* Prevent using pulseaudio on event loop

* Fix name overwrite

* Fix value
This commit is contained in:
Pascal Vizeli 2020-02-27 22:01:20 +01:00 committed by GitHub
parent 8a6ea7ab50
commit 19ca836b78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 122 additions and 103 deletions

View File

@ -29,7 +29,7 @@ from ..const import (
) )
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..exceptions import APIError from ..exceptions import APIError
from ..host.sound import SourceType from ..host.sound import StreamType
from .utils import api_process, api_process_raw, api_validate from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)
@ -115,7 +115,7 @@ class APIAudio(CoreSysAttributes):
@api_process @api_process
async def set_volume(self, request: web.Request) -> None: async def set_volume(self, request: web.Request) -> None:
"""Set audio volume on stream.""" """Set audio volume on stream."""
source: SourceType = SourceType(request.match_info.get("source")) source: StreamType = StreamType(request.match_info.get("source"))
body = await api_validate(SCHEMA_VOLUME, request) body = await api_validate(SCHEMA_VOLUME, request)
await asyncio.shield( await asyncio.shield(
@ -125,7 +125,7 @@ class APIAudio(CoreSysAttributes):
@api_process @api_process
async def set_default(self, request: web.Request) -> None: async def set_default(self, request: web.Request) -> None:
"""Set audio default stream.""" """Set audio default stream."""
source: SourceType = SourceType(request.match_info.get("source")) source: StreamType = StreamType(request.match_info.get("source"))
body = await api_validate(SCHEMA_DEFAULT, request) body = await api_validate(SCHEMA_DEFAULT, request)
await asyncio.shield(self.sys_host.sound.set_default(source, body[ATTR_NAME])) await asyncio.shield(self.sys_host.sound.set_default(source, body[ATTR_NAME]))

View File

@ -1,4 +1,5 @@
"""Pulse host control.""" """Pulse host control."""
from datetime import timedelta
from enum import Enum from enum import Enum
import logging import logging
from typing import List from typing import List
@ -8,13 +9,14 @@ from pulsectl import Pulse, PulseError, PulseIndexError, PulseOperationFailed
from ..coresys import CoreSys, CoreSysAttributes from ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import PulseAudioError from ..exceptions import PulseAudioError
from ..utils import AsyncThrottle
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)
PULSE_NAME = "supervisor" PULSE_NAME = "supervisor"
class SourceType(str, Enum): class StreamType(str, Enum):
"""INPUT/OUTPUT type of source.""" """INPUT/OUTPUT type of source."""
INPUT = "input" INPUT = "input"
@ -74,11 +76,13 @@ class SoundControl(CoreSysAttributes):
"""Return a list of available output streams.""" """Return a list of available output streams."""
return self._outputs return self._outputs
async def set_default(self, source: SourceType, name: str) -> None: async def set_default(self, stream_type: StreamType, name: str) -> None:
"""Set a stream to default input/output.""" """Set a stream to default input/output."""
def _set_default():
try: try:
with Pulse(PULSE_NAME) as pulse: with Pulse(PULSE_NAME) as pulse:
if source == SourceType.OUTPUT: if stream_type == StreamType.INPUT:
# Get source and set it as default # Get source and set it as default
source = pulse.get_source_by_name(name) source = pulse.get_source_by_name(name)
pulse.source_default_set(source) pulse.source_default_set(source)
@ -93,33 +97,41 @@ class SoundControl(CoreSysAttributes):
_LOGGER.error("Can't set %s as default: %s", name, err) _LOGGER.error("Can't set %s as default: %s", name, err)
raise PulseAudioError() from None raise PulseAudioError() from None
# Reload data # Run and Reload data
await self.sys_run_in_executor(_set_default)
await self.update() await self.update()
async def set_volume(self, source: SourceType, name: str, volume: float) -> None: async def set_volume(
self, stream_type: StreamType, name: str, volume: float
) -> None:
"""Set a stream to volume input/output.""" """Set a stream to volume input/output."""
def _set_volume():
try: try:
with Pulse(PULSE_NAME) as pulse: with Pulse(PULSE_NAME) as pulse:
if source == SourceType.OUTPUT: if stream_type == StreamType.INPUT:
# Get source and set it as default # Get source and set it as default
source = pulse.get_source_by_name(name) stream = pulse.get_source_by_name(name)
else: else:
# Get sink and set it as default # Get sink and set it as default
source = pulse.get_sink_by_name(name) stream = pulse.get_sink_by_name(name)
pulse.volume_set_all_chans(source, volume) pulse.volume_set_all_chans(stream, volume)
except PulseIndexError: except PulseIndexError:
_LOGGER.error("Can't find %s profile %s", source, name) _LOGGER.error("Can't find %s profile %s", stream_type, name)
raise PulseAudioError() from None raise PulseAudioError() from None
except PulseError as err: except PulseError as err:
_LOGGER.error("Can't set %s volume: %s", name, err) _LOGGER.error("Can't set %s volume: %s", name, err)
raise PulseAudioError() from None raise PulseAudioError() from None
# Reload data # Run and Reload data
await self.sys_run_in_executor(_set_volume)
await self.update() await self.update()
async def ativate_profile(self, card_name: str, profile_name: str) -> None: async def ativate_profile(self, card_name: str, profile_name: str) -> None:
"""Set a profile to volume input/output.""" """Set a profile to volume input/output."""
def _activate_profile():
try: try:
with Pulse(PULSE_NAME) as pulse: with Pulse(PULSE_NAME) as pulse:
card = pulse.get_sink_by_name(card_name) card = pulse.get_sink_by_name(card_name)
@ -134,12 +146,16 @@ class SoundControl(CoreSysAttributes):
) )
raise PulseAudioError() from None raise PulseAudioError() from None
# Reload data # Run and Reload data
await self.sys_run_in_executor(_activate_profile)
await self.update() await self.update()
@AsyncThrottle(timedelta(seconds=10))
async def update(self): async def update(self):
"""Update properties over dbus.""" """Update properties over dbus."""
_LOGGER.info("Update PulseAudio information") _LOGGER.info("Update PulseAudio information")
def _update():
try: try:
with Pulse(PULSE_NAME) as pulse: with Pulse(PULSE_NAME) as pulse:
server = pulse.server_info() server = pulse.server_info()
@ -197,3 +213,6 @@ class SoundControl(CoreSysAttributes):
raise PulseAudioError() from None raise PulseAudioError() from None
except PulseError as err: except PulseError as err:
_LOGGER.debug("Can't update PulseAudio data: %s", err) _LOGGER.debug("Can't update PulseAudio data: %s", err)
# Run update from pulse server
await self.sys_run_in_executor(_update)