Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More settings can be changed #1

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 69 additions & 25 deletions m27q.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,49 @@
# Based on: https://gist.github.com/wadimw/4ac972d07ed1f3b6f22a101375ecac41


import sys
import typing as t
from dataclasses import dataclass
from time import sleep
import usb.core
import usb.util
from time import sleep
import typing as t
import sys


@dataclass
class BasicProperty:
minimum: int
maximum: int
message_a: int
message_b: int = 0

def clamp(self, v: int):
return max(self.minimum, min(self.maximum, v))


@dataclass
class EnumProperty:
allowed: t.List[int]
message_a: int
message_b: int = 0

def clamp(self, v: int):
if v not in self.allowed:
raise Exception(f"Only allowed values: {self.allowed}")
return v
Comment on lines +14 to +34
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could have an abstract base property class with the common attributes plus a method that returns the list expected by set_osd.



Property = t.Union[BasicProperty, EnumProperty]


class MonitorControl:
BRIGHTNESS = BasicProperty(0, 100, 0x10, 0x00)
CONTRAST = BasicProperty(0, 100, 0x12, 0x00)
SHARPNESS = BasicProperty(0, 100, 0x87, 0x00)
BLUE_LIGHT_REDUCTION = BasicProperty(0, 10, 0xe0, 0x0b)
KVM_STATUS = BasicProperty(0, 1, 0xe0, 0x69)
BLACK_EQUALIZER = BasicProperty(0, 10, 0xe0, 0x02)
OSD_TIMEOUT = EnumProperty([5, 10, 15, 20, 25, 30], 0xe0, 0x30)

def __init__(self):
self._VID = 0x2109 # (VIA Labs, Inc.)
self._PID = 0x8883 # USB Billboard Device
Expand Down Expand Up @@ -75,44 +110,53 @@ def set_osd(self, data: bytearray):
message=bytearray([0x6E, 0x51, 0x81 + len(data), 0x03] + data),
)

def set_brightness(self, brightness: int):
self.set_osd(
[
0x10,
0x00,
max(self._min_brightness, min(self._max_brightness, brightness)),
]
)
def set_property(self, property: Property, value: int):
self.set_osd([property.message_a, property.message_b, property.clamp(value)])

def get_brightness(self):
return self.get_osd([0x10])
def get_property(self, property: Property):
return self.get_osd([property.message_a, property.message_b])

def transition_brightness(self, to_brightness: int, step: int = 3):
current_brightness = self.get_brightness()
diff = abs(to_brightness - current_brightness)
if current_brightness <= to_brightness:
def transition_property(self, property: BasicProperty, target: int, step: int = 3):
current = self.get_property(property)
diff = abs(target - current)
if current <= target:
step = 1 * step # increase
else:
step = -1 * step # decrease
while diff >= abs(step):
current_brightness += step
self.set_brightness(current_brightness)
current += step
self.set_property(property, current)
diff -= abs(step)
# Set one last time
if current_brightness != to_brightness:
self.set_brightness(to_brightness)
if current != target:
self.set_property(property, target)

def set_brightness(self, brightness: int):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this new abstraction perhaps we could leverage __getattr__ to avoid implementing all these methods, e.g.

def __getattr__(self, name):
    action, _, property_name = name.partition("_")
    property_obj = getattr(MonitorControl, property_name.upper())
    if action == "get":
        return lambda: self.get_property(property_obj)
    elif action == "set":
        return lambda v: self.set_property(property_obj, v)

    raise AttributeError()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look at doing this when I get a chance.

self.set_property(MonitorControl.BRIGHTNESS, brightness)

def get_brightness(self):
return self.get_property(MonitorControl.BRIGHTNESS)

def transition_brightness(self, to_brightness: int, step: int = 3):
self.transition_property(MonitorControl.BRIGHTNESS, to_brightness, step)

def set_volume(self, volume: int):
return self.set_osd([0x62, 0x00, volume])
self.set_property(MonitorControl.VOLUME, volume)

def get_volume(self):
return self.get_osd([0x62])
return self.get_property(MonitorControl.VOLUME)

def set_contrast(self, contrast: int):
self.set_property(MonitorControl.CONTRAST, contrast)

def get_contrast(self):
return self.get_property(MonitorControl.CONTRAST)

def get_kvm_status(self):
return self.get_osd([224, 105])
return self.get_property(MonitorControl.KVM_STATUS)

def set_kvm_status(self, status):
self.set_osd([224, 105, status])
self.set_property(MonitorControl.KVM_STATUS, status)

def toggle_kvm(self):
self.set_kvm_status(1 - self.get_kvm_status())
Expand Down