"""Bind the optional GPIO watcher to the shared reference-capture action.""" from collections.abc import Callable import logging from PyQt6.QtCore import QObject from python_app.gui.control_button import ControlButtonWatcher from python_app.gui.controllers.log_controller import LogController from python_app.models.run_config_schema import ControlButtonModel logger = logging.getLogger(__name__) class ControlButtonController(QObject): def __init__(self, parent: QObject, current_config: Callable[[], ControlButtonModel], capture_reference: Callable[[], None], log: LogController) -> None: super().__init__(parent) self._config, self._capture_reference = current_config, capture_reference self._log = log self._watcher: ControlButtonWatcher | None = None self._closed = False def start(self) -> None: if self._closed or self._watcher is not None: return config = self._config() if not config.enabled: return if config.pin < 0 or not config.gpio_chip or config.action != ControlButtonModel.ACTION_CAPTURE_TMP_REFERENCE: logger.warning("GPIO control button configuration is incomplete or unsupported; watcher not started") return watcher = None try: watcher = ControlButtonWatcher(chip=config.gpio_chip, pin=config.pin, active_low=config.active_low, bias=config.bias, debounce_ms=config.debounce_ms, parent=self) watcher.pressed.connect(self._pressed) watcher.failed.connect(self._failed) watcher.start() self._watcher = watcher logger.info("GPIO control button watching %s pin=%d action=%s", config.gpio_chip, config.pin, config.action) except Exception: logger.warning("Failed to start GPIO control button watcher", exc_info=True) if watcher is not None: self._disconnect(watcher) watcher.deleteLater() def _pressed(self) -> None: if not self._closed and self._watcher is not None: logger.info("GPIO control button pressed: capturing tmp reference") with self._log.capture_errors(): self._capture_reference() def _failed(self, message: str) -> None: logger.warning("GPIO control button watcher stopped: %s", message) def close(self) -> None: self._closed = True watcher, self._watcher = self._watcher, None if watcher is None: return self._disconnect(watcher) try: watcher.stop() except Exception: logger.warning("Error stopping GPIO control button watcher", exc_info=True) finally: self._disconnect(watcher) watcher.deleteLater() @staticmethod def _disconnect(watcher: ControlButtonWatcher) -> None: for signal in (watcher.pressed, watcher.failed): try: signal.disconnect() except (TypeError, RuntimeError): pass