Add internal KVM switch dashboard and service

This commit is contained in:
Lago
2026-03-27 14:18:36 +01:00
commit 8591e22a7b
16 changed files with 1908 additions and 0 deletions
+150
View File
@@ -0,0 +1,150 @@
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from threading import Lock
import shutil
import subprocess
import tempfile
import time
from typing import Protocol
from app.config import SUPPORTED_TARGET_PORTS
DEFAULT_DDM_PATH = Path(r"C:\Program Files\Dell\Dell Display Manager 2.0\DDM.exe")
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 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,
executable_path: Path | None = None,
max_slots: int = 8,
command_timeout_seconds: float = 4.0,
log_timeout_seconds: float = 3.0,
):
self.executable_path = executable_path or self._find_executable()
self.max_slots = max_slots
self.command_timeout_seconds = command_timeout_seconds
self.log_timeout_seconds = log_timeout_seconds
self._cached_slot: int | None = None
self._lock = Lock()
def is_available(self) -> bool:
return self.executable_path is not None and self.executable_path.exists()
def invalidate_slot(self) -> None:
with self._lock:
self._cached_slot = None
def resolve_alienware_slot(self, force: bool = False) -> int | None:
if not self.is_available():
return None
with self._lock:
if self._cached_slot is not None and not force:
return self._cached_slot
for slot in range(1, self.max_slots + 1):
result = self._run_logged_command(f"/{slot}:ReadAssetAttributes")
line = _extract_result_line(result, f"{slot}:ReadAssetAttributes")
if not line:
continue
if "INVALID COMMAND" in line.upper():
continue
if ALIENWARE_MODEL_TOKEN in line.upper():
self._cached_slot = slot
return slot
self._cached_slot = None
return None
def switch_to_port(self, slot: int, port_name: str) -> DDMCommandResult:
if not self.is_available():
return DDMCommandResult(False, "DDM.exe was not found.")
port_spec = SUPPORTED_TARGET_PORTS.get(port_name.upper())
if not port_spec:
return DDMCommandResult(False, f"Port {port_name} is not supported.")
ddm_input = str(port_spec["ddm_input"])
output = self._run_logged_command(f"/{slot}:WriteActiveInput", ddm_input)
line = _extract_result_line(output, f"{slot}:WriteActiveInput")
if line and "INVALID COMMAND" in line.upper():
return DDMCommandResult(False, "DDM rejected the WriteActiveInput command.", output)
return DDMCommandResult(
True,
f"Alienware DDM slot {slot} was instructed to switch to {ddm_input}.",
output,
)
def _run_logged_command(self, *command_args: str) -> str:
assert self.executable_path is not None
with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as handle:
log_path = Path(handle.name)
try:
subprocess.run(
[str(self.executable_path), "/Log", str(log_path), *command_args],
check=False,
capture_output=True,
text=True,
timeout=self.command_timeout_seconds,
)
deadline = time.monotonic() + self.log_timeout_seconds
while time.monotonic() < deadline:
try:
contents = log_path.read_text(encoding="utf-8", errors="replace")
except OSError:
contents = ""
if contents.strip():
return contents
time.sleep(0.1)
try:
return log_path.read_text(encoding="utf-8", errors="replace")
except OSError:
return ""
finally:
try:
log_path.unlink()
except OSError:
pass
def _find_executable(self) -> Path | None:
resolved = shutil.which("DDM.exe")
if resolved:
return Path(resolved)
if DEFAULT_DDM_PATH.exists():
return DEFAULT_DDM_PATH
return None
def _extract_result_line(text: str, prefix: str) -> str:
for line in text.splitlines():
normalized = line.strip()
if normalized.startswith(prefix):
return normalized
return ""