Sending DDC/CI commands to monitor on Windows using Python? Sending DDC/CI commands to monitor on Windows using Python? windows windows

Sending DDC/CI commands to monitor on Windows using Python?


This is easily possible using the windows monitor API. I don't think there are any Python bindings out there and pywin32 doesn't contain those functions. However, using ctypes to call them is not that hard.

Here's an example that switches the monitor to soft-off and then back on; it should be pretty easy to adapt it to changing the input source etc. The only complicated part is getting the handles for the physical monitors after all:

from ctypes import windll, byref, Structure, WinError, POINTER, WINFUNCTYPEfrom ctypes.wintypes import BOOL, HMONITOR, HDC, RECT, LPARAM, DWORD, BYTE, WCHAR, HANDLE_MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM)class _PHYSICAL_MONITOR(Structure):    _fields_ = [('handle', HANDLE),                ('description', WCHAR * 128)]def _iter_physical_monitors(close_handles=True):    """Iterates physical monitors.    The handles are closed automatically whenever the iterator is advanced.    This means that the iterator should always be fully exhausted!    If you want to keep handles e.g. because you need to store all of them and    use them later, set `close_handles` to False and close them manually."""    def callback(hmonitor, hdc, lprect, lparam):        monitors.append(HMONITOR(hmonitor))        return True    monitors = []    if not windll.user32.EnumDisplayMonitors(None, None, _MONITORENUMPROC(callback), None):        raise WinError('EnumDisplayMonitors failed')    for monitor in monitors:        # Get physical monitor count        count = DWORD()        if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)):            raise WinError()        # Get physical monitor handles        physical_array = (_PHYSICAL_MONITOR * count.value)()        if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array):            raise WinError()        for physical in physical_array:            yield physical.handle            if close_handles:                if not windll.dxva2.DestroyPhysicalMonitor(physical.handle):                    raise WinError()def set_vcp_feature(monitor, code, value):    """Sends a DDC command to the specified monitor.    See this link for a list of commands:    ftp://ftp.cis.nctu.edu.tw/pub/csie/Software/X11/private/VeSaSpEcS/VESA_Document_Center_Monitor_Interface/mccsV3.pdf    """    if not windll.dxva2.SetVCPFeature(HANDLE(monitor), BYTE(code), DWORD(value)):        raise WinError()# Switch to SOFT-OFF, wait for the user to press return and then back to ONfor handle in _iter_physical_monitors():    set_vcp_feature(handle, 0xd6, 0x04)    raw_input()    set_vcp_feature(handle, 0xd6, 0x01)


It's your lucky day, you don't have to deal with those ctypes anymore. Somebody actually made a python package that does this for you and exposes a simple api. It's called monitorcontrol.

Here's a quick example of setting luminance on all monitors:

from monitorcontrol import get_monitorsfor monitor in get_monitors():    with monitor:        monitor.set_luminance(50)