mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-24 09:36:31 +00:00
Prevent using pulseaudio on event loop (#1536)
* Prevent using pulseaudio on event loop * Fix name overwrite * Fix value
This commit is contained in:
parent
8a6ea7ab50
commit
19ca836b78
@ -29,7 +29,7 @@ from ..const import (
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import APIError
|
||||
from ..host.sound import SourceType
|
||||
from ..host.sound import StreamType
|
||||
from .utils import api_process, api_process_raw, api_validate
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
@ -115,7 +115,7 @@ class APIAudio(CoreSysAttributes):
|
||||
@api_process
|
||||
async def set_volume(self, request: web.Request) -> None:
|
||||
"""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)
|
||||
|
||||
await asyncio.shield(
|
||||
@ -125,7 +125,7 @@ class APIAudio(CoreSysAttributes):
|
||||
@api_process
|
||||
async def set_default(self, request: web.Request) -> None:
|
||||
"""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)
|
||||
|
||||
await asyncio.shield(self.sys_host.sound.set_default(source, body[ATTR_NAME]))
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Pulse host control."""
|
||||
from datetime import timedelta
|
||||
from enum import Enum
|
||||
import logging
|
||||
from typing import List
|
||||
@ -8,13 +9,14 @@ from pulsectl import Pulse, PulseError, PulseIndexError, PulseOperationFailed
|
||||
|
||||
from ..coresys import CoreSys, CoreSysAttributes
|
||||
from ..exceptions import PulseAudioError
|
||||
from ..utils import AsyncThrottle
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
PULSE_NAME = "supervisor"
|
||||
|
||||
|
||||
class SourceType(str, Enum):
|
||||
class StreamType(str, Enum):
|
||||
"""INPUT/OUTPUT type of source."""
|
||||
|
||||
INPUT = "input"
|
||||
@ -74,126 +76,143 @@ class SoundControl(CoreSysAttributes):
|
||||
"""Return a list of available output streams."""
|
||||
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."""
|
||||
try:
|
||||
with Pulse(PULSE_NAME) as pulse:
|
||||
if source == SourceType.OUTPUT:
|
||||
# Get source and set it as default
|
||||
source = pulse.get_source_by_name(name)
|
||||
pulse.source_default_set(source)
|
||||
else:
|
||||
# Get sink and set it as default
|
||||
sink = pulse.get_sink_by_name(name)
|
||||
pulse.sink_default_set(sink)
|
||||
except PulseIndexError:
|
||||
_LOGGER.error("Can't find %s profile %s", source, name)
|
||||
raise PulseAudioError() from None
|
||||
except PulseError as err:
|
||||
_LOGGER.error("Can't set %s as default: %s", name, err)
|
||||
raise PulseAudioError() from None
|
||||
|
||||
# Reload data
|
||||
def _set_default():
|
||||
try:
|
||||
with Pulse(PULSE_NAME) as pulse:
|
||||
if stream_type == StreamType.INPUT:
|
||||
# Get source and set it as default
|
||||
source = pulse.get_source_by_name(name)
|
||||
pulse.source_default_set(source)
|
||||
else:
|
||||
# Get sink and set it as default
|
||||
sink = pulse.get_sink_by_name(name)
|
||||
pulse.sink_default_set(sink)
|
||||
except PulseIndexError:
|
||||
_LOGGER.error("Can't find %s profile %s", source, name)
|
||||
raise PulseAudioError() from None
|
||||
except PulseError as err:
|
||||
_LOGGER.error("Can't set %s as default: %s", name, err)
|
||||
raise PulseAudioError() from None
|
||||
|
||||
# Run and Reload data
|
||||
await self.sys_run_in_executor(_set_default)
|
||||
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."""
|
||||
try:
|
||||
with Pulse(PULSE_NAME) as pulse:
|
||||
if source == SourceType.OUTPUT:
|
||||
# Get source and set it as default
|
||||
source = pulse.get_source_by_name(name)
|
||||
else:
|
||||
# Get sink and set it as default
|
||||
source = pulse.get_sink_by_name(name)
|
||||
|
||||
pulse.volume_set_all_chans(source, volume)
|
||||
except PulseIndexError:
|
||||
_LOGGER.error("Can't find %s profile %s", source, name)
|
||||
raise PulseAudioError() from None
|
||||
except PulseError as err:
|
||||
_LOGGER.error("Can't set %s volume: %s", name, err)
|
||||
raise PulseAudioError() from None
|
||||
def _set_volume():
|
||||
try:
|
||||
with Pulse(PULSE_NAME) as pulse:
|
||||
if stream_type == StreamType.INPUT:
|
||||
# Get source and set it as default
|
||||
stream = pulse.get_source_by_name(name)
|
||||
else:
|
||||
# Get sink and set it as default
|
||||
stream = pulse.get_sink_by_name(name)
|
||||
|
||||
# Reload data
|
||||
pulse.volume_set_all_chans(stream, volume)
|
||||
except PulseIndexError:
|
||||
_LOGGER.error("Can't find %s profile %s", stream_type, name)
|
||||
raise PulseAudioError() from None
|
||||
except PulseError as err:
|
||||
_LOGGER.error("Can't set %s volume: %s", name, err)
|
||||
raise PulseAudioError() from None
|
||||
|
||||
# Run and Reload data
|
||||
await self.sys_run_in_executor(_set_volume)
|
||||
await self.update()
|
||||
|
||||
async def ativate_profile(self, card_name: str, profile_name: str) -> None:
|
||||
"""Set a profile to volume input/output."""
|
||||
try:
|
||||
with Pulse(PULSE_NAME) as pulse:
|
||||
card = pulse.get_sink_by_name(card_name)
|
||||
pulse.card_profile_set(card, profile_name)
|
||||
|
||||
except PulseIndexError:
|
||||
_LOGGER.error("Can't find %s profile %s", card_name, profile_name)
|
||||
raise PulseAudioError() from None
|
||||
except PulseError as err:
|
||||
_LOGGER.error(
|
||||
"Can't activate %s profile %s: %s", card_name, profile_name, err
|
||||
)
|
||||
raise PulseAudioError() from None
|
||||
def _activate_profile():
|
||||
try:
|
||||
with Pulse(PULSE_NAME) as pulse:
|
||||
card = pulse.get_sink_by_name(card_name)
|
||||
pulse.card_profile_set(card, profile_name)
|
||||
|
||||
# Reload data
|
||||
except PulseIndexError:
|
||||
_LOGGER.error("Can't find %s profile %s", card_name, profile_name)
|
||||
raise PulseAudioError() from None
|
||||
except PulseError as err:
|
||||
_LOGGER.error(
|
||||
"Can't activate %s profile %s: %s", card_name, profile_name, err
|
||||
)
|
||||
raise PulseAudioError() from None
|
||||
|
||||
# Run and Reload data
|
||||
await self.sys_run_in_executor(_activate_profile)
|
||||
await self.update()
|
||||
|
||||
@AsyncThrottle(timedelta(seconds=10))
|
||||
async def update(self):
|
||||
"""Update properties over dbus."""
|
||||
_LOGGER.info("Update PulseAudio information")
|
||||
try:
|
||||
with Pulse(PULSE_NAME) as pulse:
|
||||
server = pulse.server_info()
|
||||
|
||||
# Update output
|
||||
self._outputs.clear()
|
||||
for sink in pulse.sink_list():
|
||||
self._outputs.append(
|
||||
AudioStream(
|
||||
sink.name,
|
||||
sink.description,
|
||||
sink.volume.value_flat,
|
||||
sink.name == server.default_sink_name,
|
||||
)
|
||||
)
|
||||
def _update():
|
||||
try:
|
||||
with Pulse(PULSE_NAME) as pulse:
|
||||
server = pulse.server_info()
|
||||
|
||||
# Update input
|
||||
self._inputs.clear()
|
||||
for source in pulse.source_list():
|
||||
# Filter monitor devices out because we did not use it now
|
||||
if source.name.endswith(".monitor"):
|
||||
continue
|
||||
self._inputs.append(
|
||||
AudioStream(
|
||||
source.name,
|
||||
source.description,
|
||||
source.volume.value_flat,
|
||||
source.name == server.default_source_name,
|
||||
)
|
||||
)
|
||||
|
||||
# Update Sound Card
|
||||
self._cards.clear()
|
||||
for card in pulse.card_list():
|
||||
sound_profiles: List[SoundProfile] = []
|
||||
|
||||
# Generate profiles
|
||||
for profile in card.profile_list:
|
||||
if not profile.available:
|
||||
continue
|
||||
sound_profiles.append(
|
||||
SoundProfile(
|
||||
profile.name,
|
||||
profile.description,
|
||||
profile.name == card.profile_active.name,
|
||||
# Update output
|
||||
self._outputs.clear()
|
||||
for sink in pulse.sink_list():
|
||||
self._outputs.append(
|
||||
AudioStream(
|
||||
sink.name,
|
||||
sink.description,
|
||||
sink.volume.value_flat,
|
||||
sink.name == server.default_sink_name,
|
||||
)
|
||||
)
|
||||
|
||||
self._cards.append(
|
||||
SoundCard(card.name, card.driver, sound_profiles)
|
||||
)
|
||||
# Update input
|
||||
self._inputs.clear()
|
||||
for source in pulse.source_list():
|
||||
# Filter monitor devices out because we did not use it now
|
||||
if source.name.endswith(".monitor"):
|
||||
continue
|
||||
self._inputs.append(
|
||||
AudioStream(
|
||||
source.name,
|
||||
source.description,
|
||||
source.volume.value_flat,
|
||||
source.name == server.default_source_name,
|
||||
)
|
||||
)
|
||||
|
||||
except PulseOperationFailed as err:
|
||||
_LOGGER.error("Error while processing pulse update: %s", err)
|
||||
raise PulseAudioError() from None
|
||||
except PulseError as err:
|
||||
_LOGGER.debug("Can't update PulseAudio data: %s", err)
|
||||
# Update Sound Card
|
||||
self._cards.clear()
|
||||
for card in pulse.card_list():
|
||||
sound_profiles: List[SoundProfile] = []
|
||||
|
||||
# Generate profiles
|
||||
for profile in card.profile_list:
|
||||
if not profile.available:
|
||||
continue
|
||||
sound_profiles.append(
|
||||
SoundProfile(
|
||||
profile.name,
|
||||
profile.description,
|
||||
profile.name == card.profile_active.name,
|
||||
)
|
||||
)
|
||||
|
||||
self._cards.append(
|
||||
SoundCard(card.name, card.driver, sound_profiles)
|
||||
)
|
||||
|
||||
except PulseOperationFailed as err:
|
||||
_LOGGER.error("Error while processing pulse update: %s", err)
|
||||
raise PulseAudioError() from None
|
||||
except PulseError as err:
|
||||
_LOGGER.debug("Can't update PulseAudio data: %s", err)
|
||||
|
||||
# Run update from pulse server
|
||||
await self.sys_run_in_executor(_update)
|
||||
|
Loading…
x
Reference in New Issue
Block a user