from __future__ import annotations from dataclasses import dataclass from threading import Lock from typing import Protocol from monitorcontrol import get_monitors from app.config import SUPPORTED_TARGET_PORTS ALIENWARE_MODEL_TOKEN = "AW3423DWF" @dataclass(slots=True) class DDMCommandResult: success: bool message: str raw_output: str = "" class DDMBackend(Protocol): def is_available(self) -> bool: ... def supports_monitor_targeting(self) -> bool: ... def resolve_alienware_slot(self, force: bool = False) -> int | None: ... def invalidate_slot(self) -> None: ... def switch_to_port(self, slot: int, port_name: str) -> DDMCommandResult: ... class RealDDMBackend: def __init__( self, alienware_model_token: str = ALIENWARE_MODEL_TOKEN, ): self.alienware_model_token = alienware_model_token self._cached_slot: int | None = None self._lock = Lock() def is_available(self) -> bool: _, error = self._resolve_alienware_monitor() return error is None def supports_monitor_targeting(self) -> bool: return True def invalidate_slot(self) -> None: with self._lock: self._cached_slot = None def resolve_alienware_slot(self, force: bool = False) -> int | None: with self._lock: if self._cached_slot is not None and not force: return self._cached_slot monitor, error = self._resolve_alienware_monitor() del monitor if error is not None: self._cached_slot = None return None # Service still expects a slot-like value. With monitorcontrol we # target by model, so we expose a synthetic stable slot. self._cached_slot = 1 return self._cached_slot def switch_to_port(self, slot: int, port_name: str) -> DDMCommandResult: del slot port_spec = SUPPORTED_TARGET_PORTS.get(port_name.upper()) if not port_spec: return DDMCommandResult(False, f"Port {port_name} is not supported.") target_input = _to_monitor_input_value(str(port_spec["ddm_input"])) monitor, error = self._resolve_alienware_monitor() if error is not None: return DDMCommandResult(False, error) assert monitor is not None try: with monitor: monitor.set_input_source(target_input) except Exception as exc: return DDMCommandResult( False, f"monitorcontrol failed to switch Alienware input to {target_input}: {exc}", str(exc), ) return DDMCommandResult( True, f"Alienware {self.alienware_model_token} was instructed to switch to {target_input} via monitorcontrol.", ) def _resolve_alienware_monitor(self) -> tuple[object | None, str | None]: try: monitors = list(get_monitors()) except Exception as exc: return None, f"monitorcontrol could not enumerate monitors: {exc}" candidates: list[object] = [] token = self.alienware_model_token.upper() for monitor in monitors: description = str(getattr(getattr(monitor, "vcp", None), "description", "") or str(monitor)) if token in description.upper(): candidates.append(monitor) if not candidates: return None, f"Alienware {self.alienware_model_token} was not found via monitorcontrol." if len(candidates) > 1: return None, f"Multiple Alienware {self.alienware_model_token} monitors were detected; refusing to switch." return candidates[0], None def _to_monitor_input_value(port_name: str) -> int | str: normalized = port_name.strip().upper() if normalized == "DP2": # Alienware AW3423DWF uses vendor-specific DP2 code. return 19 if normalized == "HDMI": return "HDMI1" return normalized