forked from TillRepo/IrSender
setup
This commit is contained in:
@@ -0,0 +1,464 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user