Add option to change the monitors config on the host

This commit is contained in:
Mathieu Broillet 2023-12-28 22:40:29 +01:00
parent b1e457f540
commit 32572ea5a1
Signed by: mathieu
GPG Key ID: C0E9E0E95AF03319
5 changed files with 102 additions and 5 deletions

View File

@ -14,7 +14,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_BROADCAST_ADDRESS, CONF_BROADCAST_PORT, CONF_MAC from homeassistant.const import CONF_BROADCAST_ADDRESS, CONF_BROADCAST_PORT, CONF_MAC
from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.core import HomeAssistant, ServiceCall
from .const import DOMAIN, SERVICE_SEND_MAGIC_PACKET from .const import DOMAIN, SERVICE_SEND_MAGIC_PACKET, SERVICE_CHANGE_MONITORS_CONFIG
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -6,4 +6,5 @@ SERVICE_RESTART_TO_WINDOWS_FROM_LINUX = "restart_to_windows_from_linux"
SERVICE_RESTART_TO_LINUX_FROM_WINDOWS = "restart_to_linux_from_windows" SERVICE_RESTART_TO_LINUX_FROM_WINDOWS = "restart_to_linux_from_windows"
SERVICE_PUT_COMPUTER_TO_SLEEP = "put_computer_to_sleep" SERVICE_PUT_COMPUTER_TO_SLEEP = "put_computer_to_sleep"
SERVICE_START_COMPUTER_TO_WINDOWS = "start_computer_to_windows" SERVICE_START_COMPUTER_TO_WINDOWS = "start_computer_to_windows"
RESTART_COMPUTER = "restart_computer" SERVICE_RESTART_COMPUTER = "restart_computer"
SERVICE_CHANGE_MONITORS_CONFIG = "change_monitors_config"

View File

@ -53,3 +53,16 @@ restart_computer:
target: target:
device: device:
integration: easy_computer_manage integration: easy_computer_manage
change_monitors_config:
name: Change monitors config
description: Change monitors config.
fields:
monitors_config:
name: Monitors config
description: Monitors config.
required: true
selector:
text:
target:
device:
integration: easy_computer_manage

View File

@ -35,7 +35,8 @@ 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, 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
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -114,9 +115,14 @@ async def async_setup_entry(
SERVICE_START_COMPUTER_TO_WINDOWS, SERVICE_START_COMPUTER_TO_WINDOWS,
) )
platform.async_register_entity_service( platform.async_register_entity_service(
RESTART_COMPUTER, SERVICE_RESTART_COMPUTER,
{}, {},
RESTART_COMPUTER, SERVICE_RESTART_COMPUTER,
)
platform.async_register_entity_service(
SERVICE_CHANGE_MONITORS_CONFIG,
{},
SERVICE_CHANGE_MONITORS_CONFIG,
) )
@ -249,6 +255,10 @@ class ComputerSwitch(SwitchEntity):
else: else:
utils.restart_system(self._connection) utils.restart_system(self._connection)
def change_monitors_config(self, yaml_config: str) -> None:
"""Change the monitors configuration using a YAML config file."""
utils.change_monitors_config(self._connection, yaml_config)
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

@ -1,6 +1,8 @@
import logging import logging
import re
import fabric2 import fabric2
import yaml
from fabric2 import Connection from fabric2 import Connection
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -202,3 +204,74 @@ def restart_to_windows_from_linux(connection: Connection):
_LOGGER.error( _LOGGER.error(
"Could not restart system running on %s to Windows from Linux, system does not appear to be a Linux-based OS.", "Could not restart system running on %s to Windows from Linux, system does not appear to be a Linux-based OS.",
connection.host) connection.host)
def change_monitors_config(connection: Connection, monitors_config: str):
"""From a YAML config, changes the monitors configuration on the host, only works on Linux and Gnome (for now)."""
# TODO: Add support for Windows
if is_unix_system(connection):
command_parts = ["gnome-monitor-config", "set"]
# Convert str to dict (yaml)
monitors_config = yaml.safe_load(monitors_config)
for monitor, settings in monitors_config.get('monitors', {}).items():
if settings.get('enabled', False):
if 'primary' in settings and settings['primary']:
command_parts.append(f'-LpM {monitor}')
else:
command_parts.append(f'-LM {monitor}')
if 'position' in settings:
command_parts.append(f'-x {settings["position"][0]} -y {settings["position"][1]}')
if 'mode' in settings:
command_parts.append(f'-m {settings["mode"]}')
if 'scale' in settings:
command_parts.append(f'-s {settings["scale"]}')
if 'transform' in settings:
command_parts.append(f'-t {settings["transform"]}')
command = ' '.join(command_parts)
_LOGGER.debug("Running command: %s", command)
result = connection.run(command)
if result.return_code == 0:
_LOGGER.info("Successfully changed monitors config on system running on %s.", connection.host)
else:
_LOGGER.error("Could not change monitors config on system running on %s", connection.host)
# TODO : add useful debug info
else:
_LOGGER.error("Not implemented yet.")
def parse_gnome_monitor_config(output):
# SHOULD NOT BE USED YET, STILL IN DEVELOPMENT
"""Parse the output of the gnome-monitor-config command to get the current monitor configuration."""
monitors = []
current_monitor = None
for line in output.split('\n'):
monitor_match = re.match(r'^Monitor \[ (.+?) \] (ON|OFF)$', line)
if monitor_match:
if current_monitor:
monitors.append(current_monitor)
source, status = monitor_match.groups()
current_monitor = {'source': source, 'status': status, 'names': [], 'resolutions': []}
elif current_monitor:
display_name_match = re.match(r'^\s+display-name: (.+)$', line)
resolution_match = re.match(r'^\s+(\d+x\d+@\d+(?:\.\d+)?).*$', line)
if display_name_match:
current_monitor['names'].append(display_name_match.group(1).replace('"', ''))
elif resolution_match:
current_monitor['resolutions'].append(resolution_match.group(1))
if current_monitor:
monitors.append(current_monitor)
return monitors