fix: use monitor.vcp.set_vcp_feature (monitorcontrol v4 API), fix monitor indices 1+2

This commit is contained in:
Miłosz Matysiak
2026-04-09 10:25:21 +02:00
parent 05a96e29e4
commit 5dcdfa5221
4 changed files with 31 additions and 17 deletions

View File

@@ -6,8 +6,14 @@
"name": "PC1 — DP", "name": "PC1 — DP",
"hotkey": "ctrl+alt+1", "hotkey": "ctrl+alt+1",
"monitor_inputs": [ "monitor_inputs": [
{"monitor_index": 0, "vcp_value": 15}, {
{"monitor_index": 1, "vcp_value": 15} "monitor_index": 1,
"vcp_value": 15
},
{
"monitor_index": 2,
"vcp_value": 15
}
] ]
}, },
{ {
@@ -15,8 +21,14 @@
"name": "PC2 — HDMI", "name": "PC2 — HDMI",
"hotkey": "ctrl+alt+2", "hotkey": "ctrl+alt+2",
"monitor_inputs": [ "monitor_inputs": [
{"monitor_index": 0, "vcp_value": 17}, {
{"monitor_index": 1, "vcp_value": 17} "monitor_index": 1,
"vcp_value": 17
},
{
"monitor_index": 2,
"vcp_value": 17
}
] ]
} }
], ],

View File

@@ -29,7 +29,7 @@ class MonitorSwitcher:
continue continue
try: try:
with monitors[idx] as monitor: with monitors[idx] as monitor:
monitor.set_vcp_feature(VCP_INPUT_SOURCE, vcp_value) monitor.vcp.set_vcp_feature(VCP_INPUT_SOURCE, vcp_value)
logger.info("Monitor %d -> VCP input %d", idx, vcp_value) logger.info("Monitor %d -> VCP input %d", idx, vcp_value)
except Exception as exc: except Exception as exc:
logger.warning("Monitor %d DDC/CI error: %s", idx, exc) logger.warning("Monitor %d DDC/CI error: %s", idx, exc)
@@ -46,8 +46,8 @@ class MonitorSwitcher:
for idx, monitor_handle in enumerate(get_monitors()): for idx, monitor_handle in enumerate(get_monitors()):
try: try:
with monitor_handle as monitor: with monitor_handle as monitor:
current = monitor.get_vcp_feature(VCP_INPUT_SOURCE) current = monitor.vcp.get_vcp_feature(VCP_INPUT_SOURCE)
result.append({"index": idx, "current_vcp": current.value}) result.append({"index": idx, "current_vcp": current[0]})
except Exception as exc: except Exception as exc:
logger.warning("Cannot query monitor %d: %s", idx, exc) logger.warning("Cannot query monitor %d: %s", idx, exc)
result.append({"index": idx, "current_vcp": None}) result.append({"index": idx, "current_vcp": None})

View File

@@ -10,8 +10,9 @@ print("Skanowanie monitorow (VCP code 0x60 - Input Source)...")
for i, handle in enumerate(get_monitors()): for i, handle in enumerate(get_monitors()):
with handle as monitor: with handle as monitor:
try: try:
current = monitor.get_vcp_feature(VCP_INPUT_SOURCE) current = monitor.vcp.get_vcp_feature(VCP_INPUT_SOURCE)
print(f" Monitor {i}: VCP input = {current.value} (0x{current.value:02X})") val = current[0]
print(f" Monitor {i}: VCP input = {val} (0x{val:02X})")
except Exception as e: except Exception as e:
print(f" Monitor {i}: blad odczytu - {e}") print(f" Monitor {i}: blad odczytu - {e}")
print() print()

View File

@@ -13,6 +13,7 @@ def _make_mock_monitor():
m = MagicMock() m = MagicMock()
m.__enter__ = MagicMock(return_value=m) m.__enter__ = MagicMock(return_value=m)
m.__exit__ = MagicMock(return_value=False) m.__exit__ = MagicMock(return_value=False)
m.vcp = MagicMock()
return m return m
@@ -24,8 +25,8 @@ def test_apply_profile_sends_vcp_to_both_monitors(config):
sw = MonitorSwitcher(config) sw = MonitorSwitcher(config)
sw.apply_profile("pc1_dp") sw.apply_profile("pc1_dp")
mon0.set_vcp_feature.assert_called_once_with(0x60, 15) mon0.vcp.set_vcp_feature.assert_called_once_with(0x60, 15)
mon1.set_vcp_feature.assert_called_once_with(0x60, 15) mon1.vcp.set_vcp_feature.assert_called_once_with(0x60, 15)
def test_apply_profile_hdmi(config): def test_apply_profile_hdmi(config):
@@ -36,8 +37,8 @@ def test_apply_profile_hdmi(config):
sw = MonitorSwitcher(config) sw = MonitorSwitcher(config)
sw.apply_profile("pc2_hdmi") sw.apply_profile("pc2_hdmi")
mon0.set_vcp_feature.assert_called_once_with(0x60, 17) mon0.vcp.set_vcp_feature.assert_called_once_with(0x60, 17)
mon1.set_vcp_feature.assert_called_once_with(0x60, 17) mon1.vcp.set_vcp_feature.assert_called_once_with(0x60, 17)
def test_apply_profile_skips_missing_monitor(config): def test_apply_profile_skips_missing_monitor(config):
@@ -47,19 +48,19 @@ def test_apply_profile_skips_missing_monitor(config):
sw = MonitorSwitcher(config) sw = MonitorSwitcher(config)
sw.apply_profile("pc1_dp") sw.apply_profile("pc1_dp")
mon0.set_vcp_feature.assert_called_once_with(0x60, 15) mon0.vcp.set_vcp_feature.assert_called_once_with(0x60, 15)
def test_apply_profile_continues_on_monitor_error(config): def test_apply_profile_continues_on_monitor_error(config):
mon0 = _make_mock_monitor() mon0 = _make_mock_monitor()
mon1 = _make_mock_monitor() mon1 = _make_mock_monitor()
mon0.set_vcp_feature.side_effect = Exception("DDC/CI error") mon0.vcp.set_vcp_feature.side_effect = Exception("DDC/CI error")
with patch("core.switcher.get_monitors", return_value=[mon0, mon1]): with patch("core.switcher.get_monitors", return_value=[mon0, mon1]):
sw = MonitorSwitcher(config) sw = MonitorSwitcher(config)
sw.apply_profile("pc1_dp") sw.apply_profile("pc1_dp")
mon1.set_vcp_feature.assert_called_once_with(0x60, 15) mon1.vcp.set_vcp_feature.assert_called_once_with(0x60, 15)
def test_apply_profile_unknown_profile_does_nothing(config): def test_apply_profile_unknown_profile_does_nothing(config):