200 lines
7.3 KiB
Python
200 lines
7.3 KiB
Python
import re
|
|
|
|
from custom_components.easy_computer_manager import LOGGER
|
|
from custom_components.easy_computer_manager.computer.common import CommandOutput
|
|
|
|
|
|
async def format_debug_information(computer: 'Computer'): # importing Computer causes circular import (how to fix?)
|
|
"""Return debug information about the host system."""
|
|
|
|
data = {
|
|
'os': {
|
|
'name': computer.operating_system,
|
|
'version': computer.operating_system_version,
|
|
'desktop_environment': computer.desktop_environment
|
|
},
|
|
'connection':{
|
|
'host': computer.host,
|
|
'mac': computer.mac,
|
|
'username': computer.username,
|
|
'port': computer.port,
|
|
'dualboot': computer.dualboot,
|
|
'is_on': await computer.is_on()
|
|
},
|
|
'grub':{
|
|
'windows_entry': computer.windows_entry_grub
|
|
},
|
|
'audio':{
|
|
'speakers': computer.audio_config.get('speakers'),
|
|
'microphones': computer.audio_config.get('microphones')
|
|
},
|
|
'monitors': computer.monitors_config,
|
|
'bluetooth_devices': computer.bluetooth_devices
|
|
}
|
|
|
|
return data
|
|
|
|
|
|
def parse_gnome_monitors_output(config: str) -> list:
|
|
"""
|
|
Parse the GNOME monitors configuration.
|
|
|
|
:param config:
|
|
The output of the gnome-monitor-config list command.
|
|
|
|
:type config: str
|
|
|
|
:returns: list
|
|
The parsed monitors configuration.
|
|
"""
|
|
|
|
monitors = []
|
|
current_monitor = None
|
|
|
|
for line in config.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:
|
|
# Don't include resolutions under 1280x720
|
|
if int(resolution_match.group(1).split('@')[0].split('x')[0]) >= 1280:
|
|
|
|
# If there are already resolutions in the list, check if the framerate between the last is >1
|
|
if len(current_monitor['resolutions']) > 0:
|
|
last_resolution = current_monitor['resolutions'][-1]
|
|
last_resolution_size = last_resolution.split('@')[0]
|
|
this_resolution_size = resolution_match.group(1).split('@')[0]
|
|
|
|
# Only truncate some framerates if the resolution are the same
|
|
if last_resolution_size == this_resolution_size:
|
|
last_resolution_framerate = float(last_resolution.split('@')[1])
|
|
this_resolution_framerate = float(resolution_match.group(1).split('@')[1])
|
|
|
|
# If the difference between the last resolution framerate and this one is >1, ignore it
|
|
if last_resolution_framerate - 1 > this_resolution_framerate:
|
|
current_monitor['resolutions'].append(resolution_match.group(1))
|
|
else:
|
|
# If the resolution is different, this adds the new resolution
|
|
# to the list without truncating
|
|
current_monitor['resolutions'].append(resolution_match.group(1))
|
|
else:
|
|
# This is the first resolution, add it to the list
|
|
current_monitor['resolutions'].append(resolution_match.group(1))
|
|
|
|
if current_monitor:
|
|
monitors.append(current_monitor)
|
|
|
|
return monitors
|
|
|
|
|
|
def parse_pactl_output(config_speakers: str, config_microphones: str) -> dict[str, list]:
|
|
"""
|
|
Parse the pactl audio configuration.
|
|
|
|
:param config_speakers:
|
|
The output of the pactl list sinks command.
|
|
:param config_microphones:
|
|
The output of the pactl list sources command.
|
|
|
|
:type config_speakers: str
|
|
:type config_microphones: str
|
|
|
|
:returns: dict
|
|
The parsed audio configuration.
|
|
|
|
"""
|
|
|
|
config = {'speakers': [], 'microphones': []}
|
|
|
|
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
|
|
|
|
config['speakers'] = parse_device_info(config_speakers.split('\n'), 'Sink')
|
|
config['microphones'] = parse_device_info(config_microphones.split('\n'), 'Source')
|
|
|
|
return config
|
|
|
|
|
|
def parse_bluetoothctl(command: CommandOutput, connected_devices_only: bool = True,
|
|
return_as_string: bool = False) -> list | str:
|
|
"""Parse the bluetoothctl info command.
|
|
|
|
:param command:
|
|
The command output.
|
|
:param connected_devices_only:
|
|
Only return connected devices.
|
|
Will return all devices, connected or not, if False.
|
|
|
|
:type command: :class: CommandOutput
|
|
:type connected_devices_only: bool
|
|
|
|
:returns: str | list
|
|
The parsed bluetooth devices.
|
|
|
|
"""
|
|
|
|
if not command.successful():
|
|
if command.output.__contains__("Missing device address argument"): # Means no devices are connected
|
|
return "" if return_as_string else []
|
|
else:
|
|
LOGGER.warning(f"Cannot retrieve bluetooth devices, make sure bluetoothctl is installed")
|
|
return "" if return_as_string else []
|
|
|
|
devices = []
|
|
current_device = None
|
|
|
|
for line in command.output.split('\n'):
|
|
if line.startswith('Device'):
|
|
if current_device is not None:
|
|
devices.append({
|
|
"address": current_device,
|
|
"name": current_name,
|
|
"connected": current_connected
|
|
})
|
|
current_device = line.split()[1]
|
|
current_name = None
|
|
current_connected = None
|
|
elif 'Name:' in line:
|
|
current_name = line.split(': ', 1)[1]
|
|
elif 'Connected:' in line:
|
|
current_connected = line.split(': ')[1] == 'yes'
|
|
|
|
# Add the last device if any
|
|
if current_device is not None:
|
|
devices.append({
|
|
"address": current_device,
|
|
"name": current_name,
|
|
"connected": current_connected
|
|
})
|
|
|
|
if connected_devices_only:
|
|
devices = [device for device in devices if device["connected"] == "yes"]
|
|
|
|
return devices
|