# Raspberry Pi IR Web API Kleine Python Web API für Raspberry Pi zur Ausführung definierter IR-Kommandos mittels `ir-ctl`. Features: - REST API mit API-Key - Kleine Weboberfläche ohne Login - JSON-basierte Konfiguration - Definierte Kommandos - Keine Shell Injection (`shell=False`) - Timeout pro Kommando - Logging - Geeignet für openHAB HTTP Binding - Kein Docker notwendig --- # Architektur ```text +-------------------+ | openHAB / Browser | +-------------------+ | v +-------------------+ | Python Web API | | script-api.py | +-------------------+ | v +-------------------+ | ir-ctl | | Linux IR Subsystem| +-------------------+ | v +-------------------+ | IR Sender GPIO | +-------------------+ ``` --- # Voraussetzungen ## Pakete installieren ```bash sudo apt update sudo apt install -y ir-ctl python3 ``` Optional: ```bash sudo apt install -y v4l-utils ``` --- # Dateien ```text /home/timm/IRSender/ ├── script-api.py ├── commands.json ├── apiout.log ├── blue.txt ├── brighter.txt ├── darker.txt ├── defaultColor.txt ├── fade.txt ├── green.txt ├── LichtAus.txt ├── LichtEin.txt ├── Nachtlicht.txt ├── off.txt ├── on.txt ├── purple.txt ├── red.txt ├── smooth.txt ├── white.txt └── yellow.txt ``` --- # Starten ```bash cd /home/timm/IRSender chmod +x script-api.py nohup python3 -u script-api.py ./commands.json >> apiout.log 2>&1 & ``` --- # Stoppen ```bash pkill -f script-api.py ``` --- # Logs anzeigen ```bash tail -f /home/timm/IRSender/apiout.log ``` --- # Health Check ```bash curl http://:8080/health ``` Antwort: ```json { "ok": true } ``` --- # Weboberfläche ```text http://:8080/ ``` Die Weboberfläche benötigt keinen Login. Nur Commands mit: ```json "web_enabled": true ``` werden angezeigt. --- # REST API ## Kommando ausführen ### Request ```bash curl -s -X POST \ -H "X-API-Key: change-this-global-api-key" \ -H "Content-Type: application/json" \ http://:8080/run/lirc-tv-power \ -d '{"args":{}}' ``` ### Response ```json { "ok": true, "request_id": "f8d7f6a1", "command_id": "lirc-tv-power", "exit_code": 0, "timed_out": false, "duration_ms": 124, "stdout": "", "stderr": "" } ``` --- # Kommandos anzeigen ```bash curl http://:8080/commands ``` --- # commands.json ## Beispiel ```json { "host": "0.0.0.0", "port": 8080, "commands": { "lirc-tv-power": { "description": "Send TV power button", "web_title": "TV Power", "web_enabled": true, "api_key": "change-this-global-api-key", "timeout_seconds": 10, "command": [ "ir-ctl", "--send", "/home/timm/IRSender/LichtEin.txt" ], "args": {} } } } ``` --- # Neues Kommando hinzufügen ## Beispiel ```json "my-command": { "description": "Example command", "web_title": "Example", "web_enabled": true, "api_key": "change-this-global-api-key", "timeout_seconds": 10, "command": [ "ir-ctl", "--send", "/home/timm/IRSender/example.txt" ], "args": {} } ``` --- # openHAB Integration ## HTTP Thing ### Thing ```text UID: http:url:ir_sender label: IR Sender thingTypeUID: http:url configuration: baseURL: http://:8080 commandMethod: POST contentType: application/json headers: - X-API-Key=change-this-global-api-key ``` --- ## Channel ```text channels: - id: lirc_tv_power channelTypeUID: http:switch label: TV Power configuration: mode: WRITEONLY commandExtension: /run/lirc-tv-power onValue: {"args":{}} offValue: {"args":{}} ``` --- ## Item ```text Switch IR_TV_Power "TV Power" { channel="http:url:ir_sender:lirc_tv_power" } ``` --- # Systemd Service ## Service Datei ```ini [Unit] Description=Raspberry Pi IR Web API After=network-online.target [Service] Type=simple User=timm WorkingDirectory=/home/timm/IRSender ExecStart=/usr/bin/python3 /home/timm/IRSender/script-api.py /home/timm/IRSender/commands.json Restart=always RestartSec=3 NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ProtectHome=false [Install] WantedBy=multi-user.target ``` --- ## Installation ```bash sudo nano /etc/systemd/system/rpi-ir-api.service ``` Service einfügen. Dann: ```bash sudo systemctl daemon-reload sudo systemctl enable --now rpi-ir-api ``` --- # Service Status ```bash sudo systemctl status rpi-ir-api ``` --- # Service Logs ```bash journalctl -u rpi-ir-api -f ``` --- # Sicherheit ## Implementiert - Keine Shell-Ausführung - Keine freien Kommandos - API-Key Prüfung - JSON Validierung - Timeout pro Kommando - Regex Validierung - HTML Escaping - CSRF Token für Web UI - Security Headers --- # Wichtig ## Niemals erlauben: ```json "command": ["bash", "-c", "{user_input}"] ``` oder: ```python shell=True ``` Sonst wird aus der Lichtsteuerung sehr schnell eine Fernwartung mit Spezialeffekten. --- # Troubleshooting ## 403 invalid api key API Key falsch oder Header fehlt. Prüfen: ```bash -H "X-API-Key: change-this-global-api-key" ``` --- ## 400 invalid json Body ist kein gültiges JSON. Korrekt: ```json {"args":{}} ``` --- ## 500 command failed `ir-ctl` oder Datei prüfen: ```bash ir-ctl --send /home/timm/IRSender/LichtEin.txt ``` --- ## Permission denied GPIO / IR Rechte prüfen. Test: ```bash sudo ir-ctl --send /home/timm/IRSender/LichtEin.txt ``` Falls nur mit sudo funktioniert: ```bash sudo usermod -aG video timm ``` Danach neu anmelden. --- # Backup Wichtig sichern: ```text script-api.py commands.json *.txt ``` --- # Lizenz Internes Projekt.