Add auxiliary monitor configuration and enhance trigger logic for KVM switching

This commit is contained in:
Lago
2026-03-27 15:36:37 +01:00
parent 208ad243a7
commit c8ab5ad9bc
7 changed files with 310 additions and 43 deletions
+99 -5
View File
@@ -26,15 +26,17 @@ def test_config_store_missing_file_and_roundtrip_save() -> None:
config_path = Path(tempfile.mkdtemp()) / "config.json"
store = ConfigStore(config_path)
assert store.get() == AppConfig(device_port="DP1")
assert store.get() == AppConfig(device_port="DP1", auxiliary_monitor_id=None)
assert not config_path.exists()
saved = store.save(AppConfig(device_port="HDMI"))
saved = store.save(AppConfig(device_port="HDMI", auxiliary_monitor_id="generic-pnp-monitor-1"))
assert saved.device_port == "HDMI"
assert saved.auxiliary_monitor_id == "generic-pnp-monitor-1"
assert config_path.exists()
reloaded = ConfigStore(config_path).get()
assert reloaded.device_port == "HDMI"
assert reloaded.auxiliary_monitor_id == "generic-pnp-monitor-1"
def test_config_store_loads_legacy_config_with_device_role() -> None:
@@ -46,6 +48,7 @@ def test_config_store_loads_legacy_config_with_device_role() -> None:
loaded = ConfigStore(config_path).get()
assert loaded.device_port == "DP2"
assert loaded.auxiliary_monitor_id is None
def test_api_status_and_settings_endpoints_with_fake_service() -> None:
@@ -65,8 +68,10 @@ def test_api_status_and_settings_endpoints_with_fake_service() -> None:
"samsung_session_attempt_count": 0,
"waiting_for_samsung_disconnect": False,
"trigger_input_code": None,
"active_trigger_monitor_id": None,
"trigger_target_port": None,
"trigger_matches_device_port": False,
"trigger_monitor_candidates": [],
"alienware_detected": False,
"alienware_input_code": None,
"ddm_slot": None,
@@ -85,8 +90,15 @@ def test_api_status_and_settings_endpoints_with_fake_service() -> None:
def get_status(self) -> dict[str, object]:
return self.payload
def save_settings(self, device_port: str) -> dict[str, object]:
self.payload["config"] = {"device_port": device_port}
def save_settings(
self,
device_port: str,
auxiliary_monitor_id: str | None = None,
) -> dict[str, object]:
self.payload["config"] = {
"device_port": device_port,
"auxiliary_monitor_id": auxiliary_monitor_id,
}
self.payload["last_switch_result"] = "updated"
return self.payload
@@ -98,10 +110,15 @@ def test_api_status_and_settings_endpoints_with_fake_service() -> None:
save_response = client.post(
"/api/settings",
json={"device_port": "HDMI", "device_role": "legacy_ignored"},
json={
"device_port": "HDMI",
"auxiliary_monitor_id": "generic-pnp-monitor-1",
"device_role": "legacy_ignored",
},
)
assert save_response.status_code == 200
assert save_response.json()["config"]["device_port"] == "HDMI"
assert save_response.json()["config"]["auxiliary_monitor_id"] == "generic-pnp-monitor-1"
def test_polling_switches_when_trigger_matches_device_port() -> None:
@@ -172,6 +189,83 @@ def test_polling_switches_when_trigger_matches_device_port() -> None:
assert status["last_switch_result"] == "switched"
def test_polling_uses_configured_auxiliary_monitor() -> None:
service_module = _require_module("app.service")
hardware_module = _require_module("app.hardware")
ddm_module = _require_module("app.ddm")
KvmSwitcherService = getattr(service_module, "KvmSwitcherService", None)
HardwareScan = getattr(hardware_module, "HardwareScan", None)
TriggerMonitorCandidate = getattr(hardware_module, "TriggerMonitorCandidate", None)
DDMCommandResult = getattr(ddm_module, "DDMCommandResult", None)
if (
KvmSwitcherService is None
or HardwareScan is None
or TriggerMonitorCandidate is None
or DDMCommandResult is None
):
pytest.skip("Service backend interfaces are not available yet.")
class FakeMonitorBackend:
def __init__(self) -> None:
self.call_count = 0
def scan(self):
self.call_count += 1
alienware_input = 15 if self.call_count == 1 else 19
return HardwareScan(
samsung_present=True,
trigger_input_code=None,
trigger_monitor_id=None,
trigger_candidates=[
TriggerMonitorCandidate(id="dell-u2720q-1", label="DELL U2720Q", input_code=60),
TriggerMonitorCandidate(id="generic-pnp-monitor-2", label="Generic PnP Monitor", input_code=19),
],
alienware_detected=True,
alienware_input_code=alienware_input,
errors=[
"Multiple non-Alienware DDC monitors were detected and Samsung did not expose model info; trigger monitor is ambiguous."
],
)
class FakeDDMBackend:
def __init__(self) -> None:
self.calls: list[tuple[int, str]] = []
def is_available(self) -> bool:
return True
def resolve_alienware_slot(self, force: bool = False) -> int | None:
return 1
def invalidate_slot(self) -> None:
return None
def switch_to_port(self, slot: int, port_name: str):
self.calls.append((slot, port_name))
return DDMCommandResult(True, "ok")
config_path = Path(tempfile.mkdtemp()) / "config.json"
backend = FakeDDMBackend()
service = KvmSwitcherService(
config_store=ConfigStore(config_path),
monitor_backend=FakeMonitorBackend(),
ddm_backend=backend,
poll_interval_seconds=0.01,
retry_wait_seconds=0.0,
)
status = service.save_settings(
device_port="DP2",
auxiliary_monitor_id="generic-pnp-monitor-2",
)
assert status["config"]["auxiliary_monitor_id"] == "generic-pnp-monitor-2"
assert status["active_trigger_monitor_id"] == "generic-pnp-monitor-2"
assert status["trigger_input_code"] == 19
assert status["trigger_matches_device_port"] is True
assert status["last_switch_result"] == "switched"
assert not status["errors"]
def test_polling_does_not_switch_when_trigger_does_not_match_device_port() -> None:
service_module = _require_module("app.service")
hardware_module = _require_module("app.hardware")