forked from TillRepo/IrSender
465 lines
5.7 KiB
Markdown
465 lines
5.7 KiB
Markdown
# 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://<rpi-ip>:8080/health
|
|
```
|
|
|
|
Antwort:
|
|
|
|
```json
|
|
{
|
|
"ok": true
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
# Weboberfläche
|
|
|
|
```text
|
|
http://<rpi-ip>: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://<rpi-ip>: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://<rpi-ip>: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://<rpi-ip>: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.
|