diff --git a/volume_buttons.py b/volume_buttons.py index afc4c68..12341ce 100755 --- a/volume_buttons.py +++ b/volume_buttons.py @@ -56,7 +56,7 @@ class VolumeButtonMapper: def setup(self): """Initialize the virtual device and GPIO""" - print(f"Initializing volume button mapper...") + print("Initializing volume button mapper...") # Create a virtual input device cap = { @@ -81,107 +81,70 @@ class VolumeButtonMapper: } ) - print(f"GPIO configured:") + print("GPIO configured:") print(f" - Volume Down: GPIO {VOLUME_DOWN_PIN}") print(f" - Volume Up: GPIO {VOLUME_UP_PIN}") - print(f"Press volume buttons to test...") + print("Press volume buttons to test...") - def send_volume_event(self, key_code, button_name): + def send_volume_event(self, key_code): """Send a volume event (complete press)""" self.ui.write(e.EV_KEY, key_code, 1) # Pressed self.ui.syn() self.ui.write(e.EV_KEY, key_code, 0) # Released self.ui.syn() + def _handle_button_state(self, gpio_value, button_state, press_time, last_repeat, + current_time, key_code, button_name, action_verb): + """Handle button state transitions and repetition for a single button + + Returns: (new_state, new_press_time, new_last_repeat) + """ + # ODL (Open Drain Low): INACTIVE = button pressed, ACTIVE = button released + button_pressed = (gpio_value == gpiod.line.Value.INACTIVE) + + # Transition: released -> pressed + if button_pressed and button_state == 1: + print(f"{button_name} pressed") + self.send_volume_event(key_code) + print(f" -> Volume {action_verb}") + return (0, current_time, None) + + # Transition: pressed -> released + elif not button_pressed and button_state == 0: + print(f"{button_name} released") + return (1, None, None) + + # Button still pressed - check for repetition + elif button_pressed and button_state == 0: + time_held = current_time - press_time + + if time_held >= self.HOLD_DELAY: + if last_repeat is None or (current_time - last_repeat) >= self.REPEAT_INTERVAL: + self.send_volume_event(key_code) + print(f" -> Volume {action_verb} (repeat)") + return (button_state, press_time, current_time) + + # No state change + return (button_state, press_time, last_repeat) + def check_buttons(self): """Check button states and handle automatic repetition""" try: # Read both GPIOs values = self.request.get_values([VOLUME_DOWN_PIN, VOLUME_UP_PIN]) - voldown_gpio = values[0] - volup_gpio = values[1] - current_time = time.time() - # === VOLUME DOWN MANAGEMENT === - # ODL (Open Drain Low): INACTIVE = button pressed, ACTIVE = button released - voldown_pressed = (voldown_gpio == gpiod.line.Value.INACTIVE) + # Handle volume down button + self.voldown_state, self.voldown_press_time, self.voldown_last_repeat = self._handle_button_state( + values[0], self.voldown_state, self.voldown_press_time, self.voldown_last_repeat, + current_time, e.KEY_VOLUMEDOWN, "Volume Down", "decreased" + ) - # State change detection - if voldown_pressed and self.voldown_state == 1: - # Transition released -> pressed - print("Volume Down pressed") - self.voldown_state = 0 - self.voldown_press_time = current_time - self.voldown_last_repeat = None - - # Immediate action on first press - self.send_volume_event(e.KEY_VOLUMEDOWN, "Volume Down") - print(" -> Volume decreased") - - elif not voldown_pressed and self.voldown_state == 0: - # Transition pressed -> released - print("Volume Down released") - self.voldown_state = 1 - self.voldown_press_time = None - self.voldown_last_repeat = None - - elif voldown_pressed and self.voldown_state == 0: - # Button still pressed - check if repeat needed - time_held = current_time - self.voldown_press_time - - if time_held >= self.HOLD_DELAY: - # Button held long enough - if self.voldown_last_repeat is None: - # First repetition - self.voldown_last_repeat = current_time - self.send_volume_event(e.KEY_VOLUMEDOWN, "Volume Down") - print(" -> Volume decreased (repeat)") - elif (current_time - self.voldown_last_repeat) >= self.REPEAT_INTERVAL: - # Next repetitions - self.voldown_last_repeat = current_time - self.send_volume_event(e.KEY_VOLUMEDOWN, "Volume Down") - print(" -> Volume decreased (repeat)") - - # === VOLUME UP MANAGEMENT === - # ODL (Open Drain Low): INACTIVE = button pressed, ACTIVE = button released - volup_pressed = (volup_gpio == gpiod.line.Value.INACTIVE) - - # State change detection - if volup_pressed and self.volup_state == 1: - # Transition released -> pressed - print("Volume Up pressed") - self.volup_state = 0 - self.volup_press_time = current_time - self.volup_last_repeat = None - - # Immediate action on first press - self.send_volume_event(e.KEY_VOLUMEUP, "Volume Up") - print(" -> Volume increased") - - elif not volup_pressed and self.volup_state == 0: - # Transition pressed -> released - print("Volume Up released") - self.volup_state = 1 - self.volup_press_time = None - self.volup_last_repeat = None - - elif volup_pressed and self.volup_state == 0: - # Button still pressed - check if repeat needed - time_held = current_time - self.volup_press_time - - if time_held >= self.HOLD_DELAY: - # Button held long enough - if self.volup_last_repeat is None: - # First repetition - self.volup_last_repeat = current_time - self.send_volume_event(e.KEY_VOLUMEUP, "Volume Up") - print(" -> Volume increased (repeat)") - elif (current_time - self.volup_last_repeat) >= self.REPEAT_INTERVAL: - # Next repetitions - self.volup_last_repeat = current_time - self.send_volume_event(e.KEY_VOLUMEUP, "Volume Up") - print(" -> Volume increased (repeat)") + # Handle volume up button + self.volup_state, self.volup_press_time, self.volup_last_repeat = self._handle_button_state( + values[1], self.volup_state, self.volup_press_time, self.volup_last_repeat, + current_time, e.KEY_VOLUMEUP, "Volume Up", "increased" + ) except Exception as ex: print(f"Error reading GPIO: {ex}") @@ -223,7 +186,7 @@ class VolumeButtonMapper: self.ui.close() print("Stopped cleanly.") - def signal_handler(self, signum, frame): + def signal_handler(self, _signum, _frame): """Handle signals for clean shutdown""" self.running = False