Summary
Optimize GPIO button polling interval
Problem
Aggressive polling at 100 Hz (10ms interval) unnecessarily high for human button presses, causing elevated CPU usage with no benefit to responsiveness.
Changes
Changed default poll_interval_seconds from 0.01 to 0.05 (100 Hz → 20 Hz)
Updated ButtonRuntimeConfig dataclass default
Updated fallback default in constructor
Introduced a new ButtonController responsible for handling the ReSpeaker (or generic GPIO) button:
Uses RPi.GPIO with BCM numbering and internal pull-up (PUD_UP), assuming an active-low button.
Starts a dedicated polling thread that:
Reads the GPIO level at a configurable interval (default 10 ms / 100 Hz),
Detects press and release transitions,
Computes press duration to classify short vs long press.
Short press behavior:
If state.tts_player.is_playing or state.music_player.is_playing:
Schedules state.satellite.stop() on the asyncio loop,
Also stops music via state.music_player.stop().
Otherwise:
Schedules state.satellite.manual_wakeup("button") to start a new voice interaction (wake-like behavior without needing a model instance).
Long press behavior:
Toggles mic mute using the existing event path:
Publishes set_mic_mute on the EventBus with {"state": not state.mic_muted}.
This ensures MicMuteHandler remains the single writer for mic state, updates mic_muted_event, and syncs MQTT mute state/LEDs.
Designed to fail gracefully:
If ButtonConfig.enabled is False or RPi.GPIO is unavailable, logs a message and disables button support without affecting the rest of the system.