Translation of everything into English, optimization (up to 5x less CPU-intensive), restructuring, deletion of useless judged files
This commit is contained in:
@@ -15,35 +15,9 @@ Target Hardware:
|
||||
- GPIO 4 (EC:EC_VOLUP_BTN_ODL) - Volume Up
|
||||
- Chip: /dev/gpiochip1 (cros-ec-gpio)
|
||||
|
||||
License:
|
||||
MIT License
|
||||
Copyright (c) 2025 Muyue
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Author:
|
||||
Muyue
|
||||
|
||||
Contributors:
|
||||
See CREDITS.md for acknowledgments of all contributors to this project
|
||||
and the open source libraries and tools it depends upon.
|
||||
|
||||
Repository:
|
||||
https://gitea.legion-muyue.fr/Muyue/chromebook-volume-buttons
|
||||
"""
|
||||
@@ -58,10 +32,7 @@ import signal
|
||||
CHIP_PATH = "/dev/gpiochip1" # cros-ec-gpio
|
||||
VOLUME_DOWN_PIN = 3 # EC:EC_VOLDN_BTN_ODL
|
||||
VOLUME_UP_PIN = 4 # EC:EC_VOLUP_BTN_ODL
|
||||
|
||||
# Les GPIO sont actifs à LOW (ODL = Open Drain Low)
|
||||
# Note: gpiod retourne des Value.ACTIVE/INACTIVE, pas des entiers
|
||||
POLL_INTERVAL = 0.01 # 10ms
|
||||
POLL_INTERVAL = 0.08
|
||||
|
||||
class VolumeButtonMapper:
|
||||
def __init__(self):
|
||||
@@ -69,32 +40,32 @@ class VolumeButtonMapper:
|
||||
self.request = None
|
||||
self.running = True
|
||||
|
||||
# États des boutons (1 = relâché, 0 = appuyé)
|
||||
# Button states (1 = released, 0 = pressed)
|
||||
self.voldown_state = 1
|
||||
self.volup_state = 1
|
||||
|
||||
# Timestamps pour la répétition automatique
|
||||
# Timestamps for automatic repetition
|
||||
self.voldown_press_time = None
|
||||
self.voldown_last_repeat = None
|
||||
self.volup_press_time = None
|
||||
self.volup_last_repeat = None
|
||||
|
||||
# Paramètres de répétition
|
||||
self.HOLD_DELAY = 1.0 # Délai avant répétition (1 seconde)
|
||||
self.REPEAT_INTERVAL = 0.2 # Intervalle de répétition (0.2 secondes)
|
||||
# Repetition parameters
|
||||
self.HOLD_DELAY = 0.8 # Delay before repetition (0.8 seconds)
|
||||
self.REPEAT_INTERVAL = 0.1 # Repetition interval (0.1 seconds)
|
||||
|
||||
def setup(self):
|
||||
"""Initialise le périphérique virtuel et les GPIO"""
|
||||
print(f"Initialisation du mapper de boutons volume...")
|
||||
"""Initialize the virtual device and GPIO"""
|
||||
print(f"Initializing volume button mapper...")
|
||||
|
||||
# Créer un périphérique d'entrée virtuel
|
||||
# Create a virtual input device
|
||||
cap = {
|
||||
e.EV_KEY: [e.KEY_VOLUMEDOWN, e.KEY_VOLUMEUP]
|
||||
}
|
||||
self.ui = UInput(cap, name='chromebook-volume-buttons', version=0x1)
|
||||
print(f"Périphérique virtuel créé: {self.ui.device.path}")
|
||||
print(f"Virtual device created: {self.ui.device.path}")
|
||||
|
||||
# Configurer les GPIO en lecture simple (sans edge detection)
|
||||
# Configure GPIO for simple reading (without edge detection)
|
||||
self.request = gpiod.request_lines(
|
||||
CHIP_PATH,
|
||||
consumer="volume-buttons",
|
||||
@@ -110,133 +81,133 @@ class VolumeButtonMapper:
|
||||
}
|
||||
)
|
||||
|
||||
print(f"GPIO configurés:")
|
||||
print(f"GPIO configured:")
|
||||
print(f" - Volume Down: GPIO {VOLUME_DOWN_PIN}")
|
||||
print(f" - Volume Up: GPIO {VOLUME_UP_PIN}")
|
||||
print(f"Appuyez sur les boutons volume pour tester...")
|
||||
print(f"Press volume buttons to test...")
|
||||
|
||||
def send_volume_event(self, key_code, button_name):
|
||||
"""Envoie un événement de volume (appui complet)"""
|
||||
self.ui.write(e.EV_KEY, key_code, 1) # Appuyer
|
||||
"""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) # Relâcher
|
||||
self.ui.write(e.EV_KEY, key_code, 0) # Released
|
||||
self.ui.syn()
|
||||
|
||||
def check_buttons(self):
|
||||
"""Vérifie l'état des boutons et gère la répétition automatique"""
|
||||
"""Check button states and handle automatic repetition"""
|
||||
try:
|
||||
# Lire les deux GPIO
|
||||
# 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()
|
||||
|
||||
# === GESTION VOLUME DOWN ===
|
||||
# ODL (Open Drain Low): INACTIVE = bouton appuyé, ACTIVE = bouton relâché
|
||||
# === VOLUME DOWN MANAGEMENT ===
|
||||
# ODL (Open Drain Low): INACTIVE = button pressed, ACTIVE = button released
|
||||
voldown_pressed = (voldown_gpio == gpiod.line.Value.INACTIVE)
|
||||
|
||||
# Détection du changement d'état
|
||||
# State change detection
|
||||
if voldown_pressed and self.voldown_state == 1:
|
||||
# Transition relâché -> appuyé
|
||||
print("Volume Down appuyé")
|
||||
# Transition released -> pressed
|
||||
print("Volume Down pressed")
|
||||
self.voldown_state = 0
|
||||
self.voldown_press_time = current_time
|
||||
self.voldown_last_repeat = None
|
||||
|
||||
# Action immédiate au premier appui
|
||||
# Immediate action on first press
|
||||
self.send_volume_event(e.KEY_VOLUMEDOWN, "Volume Down")
|
||||
print(" -> Son baissé")
|
||||
print(" -> Volume decreased")
|
||||
|
||||
elif not voldown_pressed and self.voldown_state == 0:
|
||||
# Transition appuyé -> relâché
|
||||
print("Volume Down relâché")
|
||||
# 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:
|
||||
# Bouton toujours appuyé - vérifier si répétition nécessaire
|
||||
# Button still pressed - check if repeat needed
|
||||
time_held = current_time - self.voldown_press_time
|
||||
|
||||
if time_held >= self.HOLD_DELAY:
|
||||
# Bouton maintenu assez longtemps
|
||||
# Button held long enough
|
||||
if self.voldown_last_repeat is None:
|
||||
# Première répétition
|
||||
# First repetition
|
||||
self.voldown_last_repeat = current_time
|
||||
self.send_volume_event(e.KEY_VOLUMEDOWN, "Volume Down")
|
||||
print(" -> Son baissé (répétition)")
|
||||
print(" -> Volume decreased (repeat)")
|
||||
elif (current_time - self.voldown_last_repeat) >= self.REPEAT_INTERVAL:
|
||||
# Répétitions suivantes
|
||||
# Next repetitions
|
||||
self.voldown_last_repeat = current_time
|
||||
self.send_volume_event(e.KEY_VOLUMEDOWN, "Volume Down")
|
||||
print(" -> Son baissé (répétition)")
|
||||
print(" -> Volume decreased (repeat)")
|
||||
|
||||
# === GESTION VOLUME UP ===
|
||||
# ODL (Open Drain Low): INACTIVE = bouton appuyé, ACTIVE = bouton relâché
|
||||
# === VOLUME UP MANAGEMENT ===
|
||||
# ODL (Open Drain Low): INACTIVE = button pressed, ACTIVE = button released
|
||||
volup_pressed = (volup_gpio == gpiod.line.Value.INACTIVE)
|
||||
|
||||
# Détection du changement d'état
|
||||
# State change detection
|
||||
if volup_pressed and self.volup_state == 1:
|
||||
# Transition relâché -> appuyé
|
||||
print("Volume Up appuyé")
|
||||
# Transition released -> pressed
|
||||
print("Volume Up pressed")
|
||||
self.volup_state = 0
|
||||
self.volup_press_time = current_time
|
||||
self.volup_last_repeat = None
|
||||
|
||||
# Action immédiate au premier appui
|
||||
# Immediate action on first press
|
||||
self.send_volume_event(e.KEY_VOLUMEUP, "Volume Up")
|
||||
print(" -> Son augmenté")
|
||||
print(" -> Volume increased")
|
||||
|
||||
elif not volup_pressed and self.volup_state == 0:
|
||||
# Transition appuyé -> relâché
|
||||
print("Volume Up relâché")
|
||||
# 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:
|
||||
# Bouton toujours appuyé - vérifier si répétition nécessaire
|
||||
# Button still pressed - check if repeat needed
|
||||
time_held = current_time - self.volup_press_time
|
||||
|
||||
if time_held >= self.HOLD_DELAY:
|
||||
# Bouton maintenu assez longtemps
|
||||
# Button held long enough
|
||||
if self.volup_last_repeat is None:
|
||||
# Première répétition
|
||||
# First repetition
|
||||
self.volup_last_repeat = current_time
|
||||
self.send_volume_event(e.KEY_VOLUMEUP, "Volume Up")
|
||||
print(" -> Son augmenté (répétition)")
|
||||
print(" -> Volume increased (repeat)")
|
||||
elif (current_time - self.volup_last_repeat) >= self.REPEAT_INTERVAL:
|
||||
# Répétitions suivantes
|
||||
# Next repetitions
|
||||
self.volup_last_repeat = current_time
|
||||
self.send_volume_event(e.KEY_VOLUMEUP, "Volume Up")
|
||||
print(" -> Son augmenté (répétition)")
|
||||
print(" -> Volume increased (repeat)")
|
||||
|
||||
except Exception as ex:
|
||||
print(f"Erreur lors de la lecture des GPIO: {ex}")
|
||||
print(f"Error reading GPIO: {ex}")
|
||||
|
||||
def run(self):
|
||||
"""Boucle principale de monitoring des GPIO"""
|
||||
"""Main GPIO monitoring loop"""
|
||||
try:
|
||||
self.setup()
|
||||
|
||||
# Boucle de polling
|
||||
# Polling loop
|
||||
while self.running:
|
||||
# Vérifier les deux boutons
|
||||
# Check both buttons
|
||||
self.check_buttons()
|
||||
|
||||
# Petit délai pour éviter de surcharger le CPU
|
||||
# Small delay to avoid overloading the CPU
|
||||
time.sleep(POLL_INTERVAL)
|
||||
|
||||
except PermissionError:
|
||||
print("\nErreur: Permission refusée.")
|
||||
print("Ce script doit être exécuté avec les privilèges root:")
|
||||
print("\nError: Permission denied.")
|
||||
print("This script must be run with root privileges:")
|
||||
print(f" sudo python3 {sys.argv[0]}")
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\nArrêt demandé par l'utilisateur...")
|
||||
print("\nStop requested by user...")
|
||||
except Exception as ex:
|
||||
print(f"\nErreur: {ex}")
|
||||
print(f"\nError: {ex}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
@@ -244,22 +215,22 @@ class VolumeButtonMapper:
|
||||
self.cleanup()
|
||||
|
||||
def cleanup(self):
|
||||
"""Nettoie les ressources"""
|
||||
print("\nNettoyage...")
|
||||
"""Clean up resources"""
|
||||
print("\nCleaning up...")
|
||||
if self.request:
|
||||
self.request.release()
|
||||
if self.ui:
|
||||
self.ui.close()
|
||||
print("Arrêté proprement.")
|
||||
print("Stopped cleanly.")
|
||||
|
||||
def signal_handler(self, signum, frame):
|
||||
"""Gère les signaux pour un arrêt propre"""
|
||||
"""Handle signals for clean shutdown"""
|
||||
self.running = False
|
||||
|
||||
def main():
|
||||
mapper = VolumeButtonMapper()
|
||||
|
||||
# Configurer les gestionnaires de signaux
|
||||
# Configure signal handlers
|
||||
signal.signal(signal.SIGINT, mapper.signal_handler)
|
||||
signal.signal(signal.SIGTERM, mapper.signal_handler)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user