import subprocess import time import threading import os import runtime.whisplay_client class WhisplayInterface: LONG_PRESS_THRESHOLD = 0.5 DOUBLE_PRESS_THRESHOLD = 0.3 def __init__(self): self.board = None self._lock = threading.Lock() self._button_press_start = None self._button_press_end = None self._button_is_pressed = False self._button_was_pressed = False self._button_is_long_pressed = False self._button_was_long_pressed = False self._button_was_double_pressed = False # Edge-detection state self._prev_is_pressed = False self._prev_press_end = None self.set_wm8960_volume_stable(speaker=250, mic=30) # -- Properties -- @property def is_pressed(self): return self._button_is_pressed @property def was_pressed(self): return self._button_was_pressed @property def is_long_pressed(self): return self._button_is_long_pressed @property def was_long_pressed(self): return self._button_was_long_pressed @property def was_double_pressed(self): return self._button_was_double_pressed @property def press_duration(self): if self._button_press_start is None: return 0.0 end = self._button_press_end if self._button_press_end is not None else time.time() return end - self._button_press_start # -- Attachment -- def create_board(self): self.board = runtime.whisplay_client.create_whisplay_hardware( app_id=os.getenv("WHISPLAY_APP_ID", "whisplay-lxst-client"), display_name="LXST Client", icon="L", exit_gesture="quad_press", use_daemon_default_log=False, ) self.board.on_button_press(self._on_button_press) self.board.on_button_release(self._on_button_release) # -- Internal press/release (called from GPIO thread) -- def _on_button_press(self): with self._lock: self._button_is_pressed = True self._button_press_start = time.time() def _on_button_release(self): with self._lock: self._button_is_pressed = False self._button_press_end = time.time() # -- Polled update (called from gesture thread) -- def update_gestures(self): self._button_was_pressed = False self._button_was_long_pressed = False self._button_was_double_pressed = False with self._lock: is_pressed = self._button_is_pressed press_start = self._button_press_start press_end = self._button_press_end now = time.time() # Press edge: transitioned from not-pressed to pressed if not self._prev_is_pressed and is_pressed: if self._prev_press_end is not None and press_start is not None: gap = press_start - self._prev_press_end if gap < WhisplayInterface.DOUBLE_PRESS_THRESHOLD: self._button_was_double_pressed = True # Release edge: transitioned from pressed to not-pressed if self._prev_is_pressed and not is_pressed: self._button_was_pressed = True if press_start is not None and press_end is not None: duration = press_end - press_start if duration >= WhisplayInterface.LONG_PRESS_THRESHOLD: self._button_was_long_pressed = True self._prev_press_end = press_end # Level flag: currently held past threshold if is_pressed and press_start is not None: self._button_is_long_pressed = (now - press_start) >= WhisplayInterface.LONG_PRESS_THRESHOLD else: self._button_is_long_pressed = False self._prev_is_pressed = is_pressed def set_wm8960_volume_stable(self, speaker: int = 0, mic: int = 0): CARD_NAME = 'wm8960soundcard' DEVICE_ARG = f'hw:{CARD_NAME}' try: subprocess.run(['amixer', '-D', DEVICE_ARG, 'sset', 'Speaker', str(speaker)], check=False, capture_output=True) subprocess.run(['amixer', '-D', DEVICE_ARG, 'sset', 'Capture', str(mic)], check=False, capture_output=True) except Exception as e: print(f"ERROR: Failed to set volume: {e}")