from StreamDock.FeatrueOption import device_type from .StreamDock import StreamDock from ..InputTypes import InputEvent, ButtonKey, EventType, KnobId, Direction from PIL import Image import os import io from ..ImageHelpers.PILHelper import * class StreamDockN3(StreamDock): """StreamDockN3 device class (N3V25 variant) - 6 LCD keys + 3 bottom buttons + 3 knobs""" KEY_COUNT = 18 KEY_MAP = False _IMAGE_KEY_MAP = { ButtonKey.KEY_1: 1, ButtonKey.KEY_2: 2, ButtonKey.KEY_3: 3, ButtonKey.KEY_4: 4, ButtonKey.KEY_5: 5, ButtonKey.KEY_6: 6, ButtonKey.KEY_7: 0x25, ButtonKey.KEY_8: 0x30, ButtonKey.KEY_9: 0x31, } _HW_TO_LOGICAL_KEY = {v: k for k, v in _IMAGE_KEY_MAP.items()} def __init__(self, transport1, devInfo): super().__init__(transport1, devInfo) def get_image_key(self, logical_key: ButtonKey) -> int: if logical_key in self._IMAGE_KEY_MAP: return self._IMAGE_KEY_MAP[logical_key] raise ValueError(f"StreamDockN3: Unsupported key {logical_key}") def decode_input_event(self, hardware_code: int, state: int) -> InputEvent: normalized_state = 1 if state == 0x01 else 0 if hardware_code in self._HW_TO_LOGICAL_KEY: return InputEvent( event_type=EventType.BUTTON, key=self._HW_TO_LOGICAL_KEY[hardware_code], state=normalized_state, ) knob_rotate_map = { 0x90: (KnobId.KNOB_1, Direction.LEFT), 0x91: (KnobId.KNOB_1, Direction.RIGHT), 0x60: (KnobId.KNOB_2, Direction.LEFT), 0x61: (KnobId.KNOB_2, Direction.RIGHT), 0x50: (KnobId.KNOB_3, Direction.LEFT), 0x51: (KnobId.KNOB_3, Direction.RIGHT), } if hardware_code in knob_rotate_map: knob_id, direction = knob_rotate_map[hardware_code] return InputEvent( event_type=EventType.KNOB_ROTATE, knob_id=knob_id, direction=direction ) knob_press_map = { 0x33: KnobId.KNOB_1, 0x34: KnobId.KNOB_2, 0x35: KnobId.KNOB_3, } if hardware_code in knob_press_map: return InputEvent( event_type=EventType.KNOB_PRESS, knob_id=knob_press_map[hardware_code], state=normalized_state, ) return InputEvent(event_type=EventType.UNKNOWN) def set_brightness(self, percent): return self.transport.set_key_brightness(percent) def set_touchscreen_image(self, path): try: if not os.path.exists(path): print(f"Error: The image file '{path}' does not exist.") return -1 image = Image.open(path) image = to_native_touchscreen_format(self, image) buf = io.BytesIO() image.save(buf, format="JPEG", quality=85) jpeg_data = buf.getvalue() res = self.transport.set_background_image_stream(jpeg_data) return res except Exception as e: print(f"Error: {e}") return -1 def set_key_image(self, key, path): try: if isinstance(key, int): if key not in range(1, 19): print(f"key '{key}' out of range. you should set (1 ~ 18)") return -1 logical_key = ButtonKey(key) else: logical_key = key if not os.path.exists(path): print(f"Error: The image file '{path}' does not exist.") return -1 hardware_key = self.get_image_key(logical_key) if hardware_key > 6: return -1 image = Image.open(path) image = to_native_key_format(self, image) buf = io.BytesIO() image.save(buf, format="JPEG", quality=90) jpeg_data = buf.getvalue() res = self.transport.set_key_image_stream(jpeg_data, hardware_key) return res except Exception as e: print(f"Error: {e}") return -1 def get_serial_number(self): return self.serial_number def key_image_format(self): return { "size": (64, 64), "format": "JPEG", "rotation": -90, "flip": (False, False), } def touchscreen_image_format(self): return { "size": (320, 240), "format": "JPEG", "rotation": -90, "flip": (False, False), } def set_device(self): self.transport.set_report_size(513, 1025, 0) self.feature_option.deviceType = device_type.dock_n3