Initial release v1.0.0
Add GPIO-based volume button driver for Lenovo IdeaPad Flex 5i Chromebook Gen 8 Features: - Volume button detection via GPIO polling - Key repeat with configurable timings - Systemd service integration - Professional documentation
This commit is contained in:
commit
bc9d6f8639
45
.gitignore
vendored
Normal file
45
.gitignore
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# Virtual Environment
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# System
|
||||||
|
.cache/
|
||||||
35
CHANGELOG.md
Normal file
35
CHANGELOG.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.0.0] - 2025-01-13
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Initial release
|
||||||
|
- GPIO-based volume button detection for Lenovo IdeaPad Flex 5i Chromebook Gen 8
|
||||||
|
- Support for Volume Up and Volume Down buttons via GPIO pins 3 and 4
|
||||||
|
- Key repeat functionality (1 second hold delay, 200ms repeat interval)
|
||||||
|
- Systemd service for automatic startup
|
||||||
|
- Installation script for easy deployment
|
||||||
|
- Comprehensive README documentation
|
||||||
|
- MIT License
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
- Python 3 implementation using gpiod and evdev libraries
|
||||||
|
- 10ms GPIO polling interval for responsive button detection
|
||||||
|
- Open Drain Low (ODL) logic handling
|
||||||
|
- Proper state machine for button press/hold/release detection
|
||||||
|
- Graceful shutdown handling with SIGINT/SIGTERM
|
||||||
|
- Virtual input device creation via uinput
|
||||||
|
|
||||||
|
### Compatibility
|
||||||
|
- Tested on Lenovo IdeaPad Flex 5i Chromebook Gen 8 (Taeko)
|
||||||
|
- Intel Core i3-1215U processor
|
||||||
|
- MrChromebox custom BIOS (version 2408.1)
|
||||||
|
- Arch Linux kernel 6.17.1
|
||||||
|
- Python 3.13.7
|
||||||
|
- libgpiod 2.2.2
|
||||||
|
- python-evdev 1.9.2
|
||||||
137
CONTRIBUTING.md
Normal file
137
CONTRIBUTING.md
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
# Contributing to Chromebook Volume Buttons Driver
|
||||||
|
|
||||||
|
Thank you for considering contributing to this project. This document provides guidelines for contributions.
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
- Be respectful and constructive
|
||||||
|
- Focus on technical merit
|
||||||
|
- Help maintain a welcoming environment
|
||||||
|
|
||||||
|
## How to Contribute
|
||||||
|
|
||||||
|
### Reporting Bugs
|
||||||
|
|
||||||
|
When reporting bugs, please include:
|
||||||
|
|
||||||
|
1. **Hardware Information:**
|
||||||
|
- Chromebook model and codename
|
||||||
|
- Processor model
|
||||||
|
- BIOS version
|
||||||
|
- Linux kernel version
|
||||||
|
|
||||||
|
2. **Software Environment:**
|
||||||
|
- Python version
|
||||||
|
- libgpiod version
|
||||||
|
- python-evdev version
|
||||||
|
|
||||||
|
3. **Issue Description:**
|
||||||
|
- Expected behavior
|
||||||
|
- Actual behavior
|
||||||
|
- Steps to reproduce
|
||||||
|
- Relevant log output
|
||||||
|
|
||||||
|
4. **GPIO Information:**
|
||||||
|
```bash
|
||||||
|
sudo gpioinfo | grep -E "VOLDN|VOLUP"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Suggesting Enhancements
|
||||||
|
|
||||||
|
Enhancement suggestions should include:
|
||||||
|
- Clear description of the feature
|
||||||
|
- Use case and motivation
|
||||||
|
- Implementation considerations
|
||||||
|
- Potential impact on existing functionality
|
||||||
|
|
||||||
|
### Pull Requests
|
||||||
|
|
||||||
|
1. **Before Starting:**
|
||||||
|
- Check existing issues and pull requests
|
||||||
|
- Discuss major changes in an issue first
|
||||||
|
- Ensure you have test hardware available
|
||||||
|
|
||||||
|
2. **Code Standards:**
|
||||||
|
- Follow PEP 8 style guide
|
||||||
|
- Use meaningful variable and function names
|
||||||
|
- Add docstrings for functions and classes
|
||||||
|
- Keep lines under 100 characters where practical
|
||||||
|
|
||||||
|
3. **Commit Guidelines:**
|
||||||
|
- Write clear, descriptive commit messages
|
||||||
|
- Use present tense ("Add feature" not "Added feature")
|
||||||
|
- Reference issues when applicable
|
||||||
|
- Keep commits focused and atomic
|
||||||
|
|
||||||
|
4. **Testing:**
|
||||||
|
- Test on actual hardware
|
||||||
|
- Verify service starts/stops correctly
|
||||||
|
- Check for resource leaks during extended operation
|
||||||
|
- Ensure CPU usage remains low
|
||||||
|
|
||||||
|
5. **Documentation:**
|
||||||
|
- Update README.md for feature changes
|
||||||
|
- Update CHANGELOG.md following Keep a Changelog format
|
||||||
|
- Add inline comments for complex logic
|
||||||
|
- Update hardware compatibility list if applicable
|
||||||
|
|
||||||
|
### Development Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone repository
|
||||||
|
git clone https://gitea.legion-muyue.fr/Muyue/chromebook-volume-buttons.git
|
||||||
|
cd chromebook-volume-buttons
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
sudo pacman -S python-evdev libgpiod
|
||||||
|
|
||||||
|
# Test script directly
|
||||||
|
sudo python3 volume_buttons.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### GPIO Mapping for New Hardware
|
||||||
|
|
||||||
|
If porting to different Chromebook models:
|
||||||
|
|
||||||
|
1. Identify GPIO chip and pins:
|
||||||
|
```bash
|
||||||
|
sudo gpioinfo
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Look for volume button GPIO lines
|
||||||
|
|
||||||
|
3. Update constants in `volume_buttons.py`:
|
||||||
|
```python
|
||||||
|
CHIP_PATH = "/dev/gpiochipX" # Update chip number
|
||||||
|
VOLUME_DOWN_PIN = X # Update pin number
|
||||||
|
VOLUME_UP_PIN = Y # Update pin number
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Test and document hardware in README.md
|
||||||
|
|
||||||
|
### Code Review Process
|
||||||
|
|
||||||
|
1. Maintainer reviews code for:
|
||||||
|
- Correctness
|
||||||
|
- Style compliance
|
||||||
|
- Documentation completeness
|
||||||
|
- Hardware compatibility impact
|
||||||
|
|
||||||
|
2. Changes may be requested before merge
|
||||||
|
|
||||||
|
3. Once approved, changes are merged to main branch
|
||||||
|
|
||||||
|
### Release Process
|
||||||
|
|
||||||
|
1. Version numbers follow Semantic Versioning
|
||||||
|
2. CHANGELOG.md updated with all changes
|
||||||
|
3. Git tag created for release
|
||||||
|
4. Release notes generated from CHANGELOG.md
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
For questions about contributing, open an issue with the "question" label.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
By contributing, you agree that your contributions will be licensed under the MIT License.
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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.
|
||||||
206
README.md
Normal file
206
README.md
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
# Chromebook Volume Buttons Driver
|
||||||
|
|
||||||
|
Hardware volume button driver for Chromebook devices with non-functional side-mounted volume controls.
|
||||||
|
|
||||||
|
## Hardware Compatibility
|
||||||
|
|
||||||
|
**Primary Target:**
|
||||||
|
- Lenovo IdeaPad Flex 5i Chromebook Gen 8
|
||||||
|
- Codename: Taeko (Google Brya family)
|
||||||
|
- Processor: Intel Core i3-1215U
|
||||||
|
- BIOS: MrChromebox custom firmware
|
||||||
|
|
||||||
|
**GPIO Configuration:**
|
||||||
|
- Volume Down: GPIO 3 (EC:EC_VOLDN_BTN_ODL)
|
||||||
|
- Volume Up: GPIO 4 (EC:EC_VOLUP_BTN_ODL)
|
||||||
|
- Chip: /dev/gpiochip1 (cros-ec-gpio)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Real-time volume button detection via GPIO polling
|
||||||
|
- Automatic key repeat after 1 second hold
|
||||||
|
- Repeat interval: 200ms
|
||||||
|
- Systemd service for automatic startup
|
||||||
|
- Low CPU overhead (10ms polling interval)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
**Required packages:**
|
||||||
|
```bash
|
||||||
|
sudo pacman -S python-evdev libgpiod
|
||||||
|
```
|
||||||
|
|
||||||
|
**Python dependencies:**
|
||||||
|
- python-gpiod (>= 2.2.0)
|
||||||
|
- python-evdev (>= 1.9.0)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Quick Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://gitea.legion-muyue.fr/Muyue/chromebook-volume-buttons.git
|
||||||
|
cd chromebook-volume-buttons
|
||||||
|
sudo bash install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Installation
|
||||||
|
|
||||||
|
1. Copy the script to system location:
|
||||||
|
```bash
|
||||||
|
sudo cp volume_buttons.py /usr/local/bin/chromebook-volume-buttons
|
||||||
|
sudo chmod +x /usr/local/bin/chromebook-volume-buttons
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install systemd service:
|
||||||
|
```bash
|
||||||
|
sudo cp chromebook-volume-buttons.service /etc/systemd/system/
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable chromebook-volume-buttons.service
|
||||||
|
sudo systemctl start chromebook-volume-buttons.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The service starts automatically on boot. Manual control:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check status
|
||||||
|
sudo systemctl status chromebook-volume-buttons.service
|
||||||
|
|
||||||
|
# View live logs
|
||||||
|
sudo journalctl -u chromebook-volume-buttons.service -f
|
||||||
|
|
||||||
|
# Restart service
|
||||||
|
sudo systemctl restart chromebook-volume-buttons.service
|
||||||
|
|
||||||
|
# Stop service
|
||||||
|
sudo systemctl stop chromebook-volume-buttons.service
|
||||||
|
|
||||||
|
# Disable auto-start
|
||||||
|
sudo systemctl disable chromebook-volume-buttons.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
The driver operates in userspace by:
|
||||||
|
1. Reading GPIO states via libgpiod (cros-ec-gpio chip)
|
||||||
|
2. Detecting button state transitions (pressed/released)
|
||||||
|
3. Emulating keyboard events via uinput (/dev/input/eventX)
|
||||||
|
4. Implementing key repeat logic for held buttons
|
||||||
|
|
||||||
|
### GPIO Signaling
|
||||||
|
|
||||||
|
The buttons use Open Drain Low (ODL) logic:
|
||||||
|
- `Value.INACTIVE` = Button pressed (LOW)
|
||||||
|
- `Value.ACTIVE` = Button released (HIGH)
|
||||||
|
|
||||||
|
### Timing Specifications
|
||||||
|
|
||||||
|
| Parameter | Value | Description |
|
||||||
|
|-----------|-------|-------------|
|
||||||
|
| Poll Interval | 10ms | GPIO sampling rate |
|
||||||
|
| Hold Delay | 1000ms | Time before repeat starts |
|
||||||
|
| Repeat Interval | 200ms | Time between repeated events |
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Buttons Not Detected
|
||||||
|
|
||||||
|
1. Verify GPIO accessibility:
|
||||||
|
```bash
|
||||||
|
sudo gpioinfo | grep -E "VOLDN|VOLUP"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
line 3: "EC:EC_VOLDN_BTN_ODL" input
|
||||||
|
line 4: "EC:EC_VOLUP_BTN_ODL" input
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Check permissions:
|
||||||
|
```bash
|
||||||
|
ls -l /dev/gpiochip1
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Verify service status:
|
||||||
|
```bash
|
||||||
|
sudo systemctl status chromebook-volume-buttons.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### High CPU Usage
|
||||||
|
|
||||||
|
Check polling interval in source code (default: 10ms = 0.01s). Adjust `POLL_INTERVAL` if needed.
|
||||||
|
|
||||||
|
### Volume Not Changing
|
||||||
|
|
||||||
|
Ensure no conflicting volume control services are running. Check with:
|
||||||
|
```bash
|
||||||
|
ps aux | grep -i volume
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Testing Without Service
|
||||||
|
|
||||||
|
Run the script directly for debugging:
|
||||||
|
```bash
|
||||||
|
sudo python3 volume_buttons.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Press Ctrl+C to stop.
|
||||||
|
|
||||||
|
### Modifying Timing
|
||||||
|
|
||||||
|
Edit the following constants in `volume_buttons.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.HOLD_DELAY = 1.0 # Seconds before repeat
|
||||||
|
self.REPEAT_INTERVAL = 0.2 # Seconds between repeats
|
||||||
|
POLL_INTERVAL = 0.01 # GPIO polling interval
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License - See LICENSE file for details.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome. Please:
|
||||||
|
1. Test on compatible hardware
|
||||||
|
2. Follow existing code style
|
||||||
|
3. Update documentation for changes
|
||||||
|
4. Submit pull requests with clear descriptions
|
||||||
|
|
||||||
|
## Hardware Variations
|
||||||
|
|
||||||
|
If your Chromebook model differs, check GPIO mappings:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo gpioinfo
|
||||||
|
```
|
||||||
|
|
||||||
|
Look for volume button GPIO lines and update constants in the script:
|
||||||
|
```python
|
||||||
|
VOLUME_DOWN_PIN = 3 # Update if different
|
||||||
|
VOLUME_UP_PIN = 4 # Update if different
|
||||||
|
```
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- ChromiumOS EC GPIO Documentation
|
||||||
|
- Linux GPIO Userspace API (libgpiod)
|
||||||
|
- Python evdev Documentation
|
||||||
|
- MrChromebox Firmware
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Muyue
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
- MrChromebox for custom BIOS support
|
||||||
|
- ChromiumOS embedded controller team
|
||||||
|
- Linux GPIO subsystem maintainers
|
||||||
19
chromebook-volume-buttons.service
Normal file
19
chromebook-volume-buttons.service
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Chromebook Volume Buttons Handler
|
||||||
|
Documentation=https://github.com/chromebook-volume-buttons
|
||||||
|
After=multi-user.target
|
||||||
|
Wants=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/python3 /usr/local/bin/chromebook-volume-buttons
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
User=root
|
||||||
|
|
||||||
|
# Security settings
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
56
install.sh
Executable file
56
install.sh
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Installation script for Chromebook Volume Buttons Driver
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
echo "=== Chromebook Volume Buttons Driver Installation ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "Error: This script must be run with sudo privileges"
|
||||||
|
echo "Usage: sudo bash install.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy Python script to system location
|
||||||
|
echo "[1/5] Installing Python script to /usr/local/bin/..."
|
||||||
|
cp "${SCRIPT_DIR}/volume_buttons.py" /usr/local/bin/chromebook-volume-buttons
|
||||||
|
chmod +x /usr/local/bin/chromebook-volume-buttons
|
||||||
|
echo " Script installed successfully"
|
||||||
|
|
||||||
|
# Install systemd service
|
||||||
|
echo "[2/5] Installing systemd service..."
|
||||||
|
cp "${SCRIPT_DIR}/chromebook-volume-buttons.service" /etc/systemd/system/
|
||||||
|
echo " Service file installed"
|
||||||
|
|
||||||
|
# Reload systemd daemon
|
||||||
|
echo "[3/5] Reloading systemd daemon..."
|
||||||
|
systemctl daemon-reload
|
||||||
|
echo " Daemon reloaded"
|
||||||
|
|
||||||
|
# Enable service for auto-start
|
||||||
|
echo "[4/5] Enabling service for automatic startup..."
|
||||||
|
systemctl enable chromebook-volume-buttons.service
|
||||||
|
echo " Service enabled (will start on boot)"
|
||||||
|
|
||||||
|
# Start service immediately
|
||||||
|
echo "[5/5] Starting service..."
|
||||||
|
systemctl start chromebook-volume-buttons.service
|
||||||
|
echo " Service started"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Installation completed successfully ==="
|
||||||
|
echo ""
|
||||||
|
echo "Service status:"
|
||||||
|
systemctl status chromebook-volume-buttons.service --no-pager -l || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Useful commands:"
|
||||||
|
echo " View live logs: sudo journalctl -u chromebook-volume-buttons.service -f"
|
||||||
|
echo " Stop service: sudo systemctl stop chromebook-volume-buttons.service"
|
||||||
|
echo " Restart service: sudo systemctl restart chromebook-volume-buttons.service"
|
||||||
|
echo " Disable service: sudo systemctl disable chromebook-volume-buttons.service"
|
||||||
|
echo " Check status: sudo systemctl status chromebook-volume-buttons.service"
|
||||||
226
volume_buttons.py
Executable file
226
volume_buttons.py
Executable file
@ -0,0 +1,226 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Script pour activer les boutons volume du Chromebook Taeko
|
||||||
|
Les boutons sont connectés aux GPIO 3 (Volume Down) et 4 (Volume Up)
|
||||||
|
sur le chip cros-ec-gpio (gpiochip1)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import gpiod
|
||||||
|
from evdev import UInput, ecodes as e
|
||||||
|
import time
|
||||||
|
import signal
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
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
|
||||||
|
|
||||||
|
class VolumeButtonMapper:
|
||||||
|
def __init__(self):
|
||||||
|
self.ui = None
|
||||||
|
self.request = None
|
||||||
|
self.running = True
|
||||||
|
|
||||||
|
# États des boutons (1 = relâché, 0 = appuyé)
|
||||||
|
self.voldown_state = 1
|
||||||
|
self.volup_state = 1
|
||||||
|
|
||||||
|
# Timestamps pour la répétition automatique
|
||||||
|
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)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
"""Initialise le périphérique virtuel et les GPIO"""
|
||||||
|
print(f"Initialisation du mapper de boutons volume...")
|
||||||
|
|
||||||
|
# Créer un périphérique d'entrée virtuel
|
||||||
|
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}")
|
||||||
|
|
||||||
|
# Configurer les GPIO en lecture simple (sans edge detection)
|
||||||
|
self.request = gpiod.request_lines(
|
||||||
|
CHIP_PATH,
|
||||||
|
consumer="volume-buttons",
|
||||||
|
config={
|
||||||
|
VOLUME_DOWN_PIN: gpiod.LineSettings(
|
||||||
|
direction=gpiod.line.Direction.INPUT,
|
||||||
|
bias=gpiod.line.Bias.PULL_UP
|
||||||
|
),
|
||||||
|
VOLUME_UP_PIN: gpiod.LineSettings(
|
||||||
|
direction=gpiod.line.Direction.INPUT,
|
||||||
|
bias=gpiod.line.Bias.PULL_UP
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"GPIO configurés:")
|
||||||
|
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...")
|
||||||
|
|
||||||
|
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
|
||||||
|
self.ui.syn()
|
||||||
|
self.ui.write(e.EV_KEY, key_code, 0) # Relâcher
|
||||||
|
self.ui.syn()
|
||||||
|
|
||||||
|
def check_buttons(self):
|
||||||
|
"""Vérifie l'état des boutons et gère la répétition automatique"""
|
||||||
|
try:
|
||||||
|
# Lire les deux GPIO
|
||||||
|
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é
|
||||||
|
voldown_pressed = (voldown_gpio == gpiod.line.Value.INACTIVE)
|
||||||
|
|
||||||
|
# Détection du changement d'état
|
||||||
|
if voldown_pressed and self.voldown_state == 1:
|
||||||
|
# Transition relâché -> appuyé
|
||||||
|
print("Volume Down appuyé")
|
||||||
|
self.voldown_state = 0
|
||||||
|
self.voldown_press_time = current_time
|
||||||
|
self.voldown_last_repeat = None
|
||||||
|
|
||||||
|
# Action immédiate au premier appui
|
||||||
|
self.send_volume_event(e.KEY_VOLUMEDOWN, "Volume Down")
|
||||||
|
print(" -> Son baissé")
|
||||||
|
|
||||||
|
elif not voldown_pressed and self.voldown_state == 0:
|
||||||
|
# Transition appuyé -> relâché
|
||||||
|
print("Volume Down relâché")
|
||||||
|
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
|
||||||
|
time_held = current_time - self.voldown_press_time
|
||||||
|
|
||||||
|
if time_held >= self.HOLD_DELAY:
|
||||||
|
# Bouton maintenu assez longtemps
|
||||||
|
if self.voldown_last_repeat is None:
|
||||||
|
# Première répétition
|
||||||
|
self.voldown_last_repeat = current_time
|
||||||
|
self.send_volume_event(e.KEY_VOLUMEDOWN, "Volume Down")
|
||||||
|
print(" -> Son baissé (répétition)")
|
||||||
|
elif (current_time - self.voldown_last_repeat) >= self.REPEAT_INTERVAL:
|
||||||
|
# Répétitions suivantes
|
||||||
|
self.voldown_last_repeat = current_time
|
||||||
|
self.send_volume_event(e.KEY_VOLUMEDOWN, "Volume Down")
|
||||||
|
print(" -> Son baissé (répétition)")
|
||||||
|
|
||||||
|
# === GESTION VOLUME UP ===
|
||||||
|
# ODL (Open Drain Low): INACTIVE = bouton appuyé, ACTIVE = bouton relâché
|
||||||
|
volup_pressed = (volup_gpio == gpiod.line.Value.INACTIVE)
|
||||||
|
|
||||||
|
# Détection du changement d'état
|
||||||
|
if volup_pressed and self.volup_state == 1:
|
||||||
|
# Transition relâché -> appuyé
|
||||||
|
print("Volume Up appuyé")
|
||||||
|
self.volup_state = 0
|
||||||
|
self.volup_press_time = current_time
|
||||||
|
self.volup_last_repeat = None
|
||||||
|
|
||||||
|
# Action immédiate au premier appui
|
||||||
|
self.send_volume_event(e.KEY_VOLUMEUP, "Volume Up")
|
||||||
|
print(" -> Son augmenté")
|
||||||
|
|
||||||
|
elif not volup_pressed and self.volup_state == 0:
|
||||||
|
# Transition appuyé -> relâché
|
||||||
|
print("Volume Up relâché")
|
||||||
|
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
|
||||||
|
time_held = current_time - self.volup_press_time
|
||||||
|
|
||||||
|
if time_held >= self.HOLD_DELAY:
|
||||||
|
# Bouton maintenu assez longtemps
|
||||||
|
if self.volup_last_repeat is None:
|
||||||
|
# Première répétition
|
||||||
|
self.volup_last_repeat = current_time
|
||||||
|
self.send_volume_event(e.KEY_VOLUMEUP, "Volume Up")
|
||||||
|
print(" -> Son augmenté (répétition)")
|
||||||
|
elif (current_time - self.volup_last_repeat) >= self.REPEAT_INTERVAL:
|
||||||
|
# Répétitions suivantes
|
||||||
|
self.volup_last_repeat = current_time
|
||||||
|
self.send_volume_event(e.KEY_VOLUMEUP, "Volume Up")
|
||||||
|
print(" -> Son augmenté (répétition)")
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"Erreur lors de la lecture des GPIO: {ex}")
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Boucle principale de monitoring des GPIO"""
|
||||||
|
try:
|
||||||
|
self.setup()
|
||||||
|
|
||||||
|
# Boucle de polling
|
||||||
|
while self.running:
|
||||||
|
# Vérifier les deux boutons
|
||||||
|
self.check_buttons()
|
||||||
|
|
||||||
|
# Petit délai pour éviter de surcharger le 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(f" sudo python3 {sys.argv[0]}")
|
||||||
|
sys.exit(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nArrêt demandé par l'utilisateur...")
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"\nErreur: {ex}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.exit(1)
|
||||||
|
finally:
|
||||||
|
self.cleanup()
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
"""Nettoie les ressources"""
|
||||||
|
print("\nNettoyage...")
|
||||||
|
if self.request:
|
||||||
|
self.request.release()
|
||||||
|
if self.ui:
|
||||||
|
self.ui.close()
|
||||||
|
print("Arrêté proprement.")
|
||||||
|
|
||||||
|
def signal_handler(self, signum, frame):
|
||||||
|
"""Gère les signaux pour un arrêt propre"""
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
mapper = VolumeButtonMapper()
|
||||||
|
|
||||||
|
# Configurer les gestionnaires de signaux
|
||||||
|
signal.signal(signal.SIGINT, mapper.signal_handler)
|
||||||
|
signal.signal(signal.SIGTERM, mapper.signal_handler)
|
||||||
|
|
||||||
|
mapper.run()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
x
Reference in New Issue
Block a user