add audio config service (not tested yet)

This commit is contained in:
Mathieu Broillet 2023-12-30 18:45:14 +01:00
parent adc3ea0211
commit 258ea2b9b6
Signed by: mathieu
GPG Key ID: C0E9E0E95AF03319
4 changed files with 150 additions and 8 deletions

View File

@ -9,3 +9,4 @@ SERVICE_START_COMPUTER_TO_WINDOWS = "start_computer_to_windows"
SERVICE_RESTART_COMPUTER = "restart_computer" SERVICE_RESTART_COMPUTER = "restart_computer"
SERVICE_CHANGE_MONITORS_CONFIG = "change_monitors_config" SERVICE_CHANGE_MONITORS_CONFIG = "change_monitors_config"
SERVICE_STEAM_BIG_PICTURE = "steam_big_picture" SERVICE_STEAM_BIG_PICTURE = "steam_big_picture"
SERVICE_CHANGE_AUDIO_CONFIG = "change_audio_config"

View File

@ -97,3 +97,37 @@ steam_big_picture:
value: stop value: stop
- label: Exit and go back to the desktop Steam UI - label: Exit and go back to the desktop Steam UI
value: exit value: exit
change_audio_config:
name: Change audio config
description: Change audio config (volume, mute, input, output).
target:
entity:
integration: easy_computer_manager
domain: switch
fields:
volume:
name: Volume
description: The volume to set.
example: 50
selector:
number:
min: 0
max: 100
mute:
name: Mute
description: Mute the audio.
example: true
selector:
boolean:
input_device:
name: Input device
description: The ID/name/description of the input device.
example: "Kraken 7.1 Chroma Stéréo analogique"
selector:
text:
output_output:
name: Output device
description: The ID/name/description of the output device.
example: "Starship/Matisse HD Audio Controller Stéréo analogique"
selector:
text:

View File

@ -37,7 +37,7 @@ from paramiko.ssh_exception import AuthenticationException
from . import utils from . import utils
from .const import SERVICE_RESTART_TO_WINDOWS_FROM_LINUX, SERVICE_PUT_COMPUTER_TO_SLEEP, \ from .const import SERVICE_RESTART_TO_WINDOWS_FROM_LINUX, SERVICE_PUT_COMPUTER_TO_SLEEP, \
SERVICE_START_COMPUTER_TO_WINDOWS, SERVICE_RESTART_COMPUTER, SERVICE_RESTART_TO_LINUX_FROM_WINDOWS, \ SERVICE_START_COMPUTER_TO_WINDOWS, SERVICE_RESTART_COMPUTER, SERVICE_RESTART_TO_LINUX_FROM_WINDOWS, \
SERVICE_CHANGE_MONITORS_CONFIG, SERVICE_STEAM_BIG_PICTURE SERVICE_CHANGE_MONITORS_CONFIG, SERVICE_STEAM_BIG_PICTURE, SERVICE_CHANGE_AUDIO_CONFIG
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -96,19 +96,20 @@ async def async_setup_entry(
platform = entity_platform.async_get_current_platform() platform = entity_platform.async_get_current_platform()
services = [SERVICE_RESTART_TO_WINDOWS_FROM_LINUX, basic_services = [SERVICE_RESTART_TO_WINDOWS_FROM_LINUX,
SERVICE_RESTART_TO_LINUX_FROM_WINDOWS, SERVICE_RESTART_TO_LINUX_FROM_WINDOWS,
SERVICE_PUT_COMPUTER_TO_SLEEP, SERVICE_PUT_COMPUTER_TO_SLEEP,
SERVICE_START_COMPUTER_TO_WINDOWS, SERVICE_START_COMPUTER_TO_WINDOWS,
SERVICE_RESTART_COMPUTER] SERVICE_RESTART_COMPUTER]
for service in services: for service in basic_services:
platform.async_register_entity_service( platform.async_register_entity_service(
service, service,
{}, {},
service, service,
) )
# Register the service to change the monitors configuration
platform.async_register_entity_service( platform.async_register_entity_service(
SERVICE_CHANGE_MONITORS_CONFIG, SERVICE_CHANGE_MONITORS_CONFIG,
make_entity_service_schema( make_entity_service_schema(
@ -116,6 +117,8 @@ async def async_setup_entry(
), ),
SERVICE_CHANGE_MONITORS_CONFIG, SERVICE_CHANGE_MONITORS_CONFIG,
) )
# Register the service to control Steam Big Picture mode
platform.async_register_entity_service( platform.async_register_entity_service(
SERVICE_STEAM_BIG_PICTURE, SERVICE_STEAM_BIG_PICTURE,
make_entity_service_schema( make_entity_service_schema(
@ -124,6 +127,18 @@ async def async_setup_entry(
SERVICE_STEAM_BIG_PICTURE, SERVICE_STEAM_BIG_PICTURE,
) )
# Register the service to change the audio configuration
platform.async_register_entity_service(
SERVICE_CHANGE_AUDIO_CONFIG,
make_entity_service_schema(
{vol.Optional("volume"): int,
vol.Optional("mute"): bool,
vol.Optional("input_device"): str,
vol.Optional("output_device"): str}
),
SERVICE_CHANGE_AUDIO_CONFIG,
)
class ComputerSwitch(SwitchEntity): class ComputerSwitch(SwitchEntity):
"""Representation of a computer switch.""" """Representation of a computer switch."""
@ -269,6 +284,10 @@ class ComputerSwitch(SwitchEntity):
else: else:
raise HomeAssistantError("You must specify an action.") raise HomeAssistantError("You must specify an action.")
def change_audio_config(self, volume: int, mute: bool, input_device: str, output_device: str) -> None:
"""Change the audio configuration using a YAML config file."""
utils.change_audio_config(self._connection, volume, mute, input_device, output_device)
def update(self) -> None: def update(self) -> None:
"""Ping the computer to see if it is online and update the state.""" """Ping the computer to see if it is online and update the state."""
ping_cmd = [ ping_cmd = [

View File

@ -350,3 +350,91 @@ def steam_big_picture(connection: Connection, action: str):
if result is None or result.return_code != 0: if result is None or result.return_code != 0:
raise HomeAssistantError(f"Could not {action} Steam Big Picture on system running at {connection.host}.") raise HomeAssistantError(f"Could not {action} Steam Big Picture on system running at {connection.host}.")
def get_audio_config(connection: Connection):
if is_unix_system(connection):
config = {'sinks': [], 'sources': []}
def parse_device_info(lines, device_type):
devices = []
current_device = {}
for line in lines:
if line.startswith(f"{device_type} #"):
if current_device and "Monitor" not in current_device['description']:
devices.append(current_device)
current_device = {'id': int(re.search(r'#(\d+)', line).group(1))}
elif line.startswith(" Name:"):
current_device['name'] = line.split(":")[1].strip()
elif line.startswith(" State:"):
current_device['state'] = line.split(":")[1].strip()
elif line.startswith(" Description:"):
current_device['description'] = line.split(":")[1].strip()
if current_device:
devices.append(current_device)
return devices
# Get sinks
result = connection.run("LANG=en_US.UTF-8 pactl list sinks")
if result.return_code != 0:
raise HomeAssistantError(f"Could not get audio sinks on system running at {connection.host}.")
config['sinks'] = parse_device_info(result.stdout.split('\n'), 'Sink')
# Get sources
result = connection.run("LANG=en_US.UTF-8 pactl list sources")
if result.return_code != 0:
raise HomeAssistantError(f"Could not get audio sources on system running at {connection.host}.")
config['sources'] = parse_device_info(result.stdout.split('\n'), 'Source')
return config
else:
raise HomeAssistantError("Not implemented yet for Windows OS.")
def change_audio_config(connection: Connection, volume: int, mute: bool, input_device: str = "@DEFAULT_SOURCE@",
output_device: str = "@DEFAULT_SINK@"):
"""Change audio configuration on the host system."""
if is_unix_system(connection):
current_config = get_audio_config(connection)
executable = "pactl"
commands = []
def get_device_id(device_type, user_device):
for device in current_config[device_type]:
if device['description'] == user_device:
return device['name']
return user_device
# Set default sink if specified
if output_device and output_device != "@DEFAULT_SINK@":
output_device = get_device_id('sinks', output_device)
commands.append(f"{executable} set-default-sink {output_device}")
# Set default source if specified
if input_device and input_device != "@DEFAULT_SOURCE@":
input_device = get_device_id('sources', input_device)
commands.append(f"{executable} set-default-source {input_device}")
# Set sink volume if specified
if volume is not None:
commands.append(f"{executable} set-sink-volume {output_device} {volume}%")
# Set sink and source mute status if specified
if mute is not None:
commands.append(f"{executable} set-sink-mute {output_device} {'yes' if mute else 'no'}")
commands.append(f"{executable} set-source-mute {output_device} {'yes' if mute else 'no'}")
# Execute commands
for command in commands:
_LOGGER.debug("Running command: %s", command)
result = connection.run(command)
if result.return_code != 0:
raise HomeAssistantError("Could not change audio config on system running on %s, check logs with debug",
connection.host)
else:
raise HomeAssistantError("Not implemented yet for Windows OS.")