diff --git a/README.md b/README.md index c714f1c..9c28e38 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,176 @@ -# 7.3" ePaper Dashboard +# epaper-dashboard -Plugin-basiertes Dashboard für Waveshare 7.3-inch ACeP 7-Color e-Paper (F) HAT auf einem Raspberry Pi 4. +Plugin-based dashboard for the **Waveshare 7.3" ACeP 7-Color e-Paper (F) HAT** on Raspberry Pi 4. + +Rendert alle 180s (oder konfigurierbar) ein 800×480px Vollbild auf dem Display. + +## Hardware + +- **Display:** Waveshare 7.3inch ACeP 7-Color e-Paper (F) HAT — 800×480, 7 Farben +- **Pi:** Raspberry Pi 4 (oder Zero 2 W mit Einschränkungen) +- **Interface:** SPI ## Architektur -- **Renderer (`dashboard.py`)**: Lädt `config.json`, instanziiert Plugins, refresht das Display alle `refresh_interval_s` Sekunden (≥180 empfohlen). Reagiert auf Live-Trigger via Unix-Socket. -- **Web-Admin (`admin.py`)**: HTTP Basic Auth, konfiguriert Slots, Plugins und Secrets im Browser. Triggert Live-Refresh. -- **Plugins (`plugins/`)**: Jedes Widget ist ein Python-Modul mit `Widget`-Klasse (siehe `plugins/base.py`). Eigenes Plugin = eine Datei in `plugins/`. -- **Templates (`templates/index.html`)**: Single-Page-UI, auto-reload-fähig. - -## Setup - -```bash -# Auf dem Pi: -sudo apt install python3-pil python3-numpy python3-flask git -git clone # oder rsync von deinem Dev-Rechner -cd port/ -# SPI in /boot/firmware/config.txt aktivieren (dtparam=spi=on) -# Reboot -python3 admin.py & -python3 dashboard.py & +``` +┌─────────────────────────────────────────────┐ +│ epaper-dashboard │ +├──────────────────┬──────────────────────────┤ +│ dashboard.py │ admin.py │ +│ (Renderer) │ (Web Admin UI :8080) │ +├──────────────────┴──────────────────────────┤ +│ layout.py · 4×4 Grid · Pack-Algorithmus │ +├────────────────────────────────────────────┤ +│ plugins/ (eines pro Widget) │ +│ clock · weather · system · spotify │ +│ strava · gmail · minimax · hello │ +├────────────────────────────────────────────┤ +│ network_watchdog.py │ +│ (WiFi AP/Client Management) │ +├────────────────────────────────────────────┤ +│ waveshare_epd/ (Vendor-Treiber) │ +│ epd7in3f.py │ +└─────────────────────────────────────────────┘ ``` -Web-UI: -Default Login: `admin` / `admin` — bitte `EPAPER_ADMIN_PASSWORD` in `.env` setzen. +## Quick Start -## Plugin schreiben +### Pi vorbereiten -Eine Datei `plugins/my_widget.py`: +```bash +sudo apt update && sudo apt install -y python3-pil python3-numpy python3-flask git +sudo raspi-config # → Interface Options → SPI → Enable +sudo reboot +``` + +### Setup + +```bash +# Repository klonen +git clone https://git.pkop.de/Vibecode/epaper-dashboard.git +cd epaper-dashboard + +# Config erstellen +cp config.example.json config.json +# → config.json editieren (Plugins + Layout) + +#waveshare_epd Treiber +git clone https://github.com/waveshareteam/Waveshare_GPIO.git waveshare_epd_lib +# oder: den waveshare_epd Ordner vom vorherigen Setup übernehmen + +# Services installieren +chmod +x install_services.sh +./install_services.sh +``` + +### Config + +`config.json` — Version 2 Format (empfohlen): + +```json +{ + "version": 2, + "refresh_interval_s": 180, + "layout": { + "grid": { "cols": 4, "rows": 4 }, + "items": [ + { "id": "c1", "plugin": "clock", "x": 0, "y": 0, "w": 2, "h": 2 }, + { "id": "w1", "plugin": "weather", "x": 2, "y": 0, "w": 2, "h": 2 }, + { "id": "st1", "plugin": "system", "x": 0, "y": 2, "w": 2, "h": 2 }, + { "id": "h1", "plugin": "hello", "x": 2, "y": 2, "w": 2, "h": 2 } + ] + }, + "plugin_configs": { + "hello": { "text": "Edit me!", "size": 40 } + } +} +``` + +**Grid:** 4 Spalten × 4 Zeilen = 16 Zellen à 200×120px. `x,y` = Spalte/Zeile oben-links, `w,h` = Breite/Höhe in Zellen. + +### Web Admin + +``` +http://pi:8080/ +``` + +- **Layout-Editor** — Items per Klick hinzufügen, verschieben, Größe ändern, Auto-Pack +- **Plugin-Config** — API-Keys, Locations, etc. pro Widget +- **Refresh Now** — sofortiger Display-Refresh +- **Netzwerk** — WLAN wechseln ohne SSH + +Default Login: `admin` / `admin` — in `.env` mit `EPAPER_ADMIN_PASSWORD` setzen. + +## Plugins + +| Plugin | Datenquelle | Config-Felder | +|--------|-------------|---------------| +| `clock` | Systemzeit | — | +| `weather` | Open-Meteo (kein API-Key) | `location` (lat,lon), `show_uv`, `show_forecast_hours` | +| `system` | Pi CPU/RAM/Uptime | `show_uptime`, `show_load`, `show_temp` | +| `spotify` | Spotify Web API | `client_id`, `client_secret` (via Spotify Dev Portal) | +| `strava` | Strava API | `access_token`, `club_id` | +| `gmail` | Gmail API | `credentials_json` (OAuth2) | +| `minimax` | MiniMax AI | `api_key`, `model`, `prompt` | +| `hello` | statisch | `text`, `color`, `size` | + +## Eigenes Plugin schreiben ```python +# plugins/my_widget.py from plugins.base import Widget -from palette import FG, INFO +from palette import FG, BG class Widget(Widget): name = "my_widget" label = "Mein Widget" - description = "Zeigt etwas Nützliches" + description = "Zeigt etwas" category = "info" - + config_schema = [ {"key": "api_key", "label": "API Key", "type": "secret"}, - {"key": "interval", "label": "Intervall (Sekunden)", "type": "int", "default": 60}, - {"key": "show_extra", "label": "Extra anzeigen", "type": "bool", "default": True}, + {"key": "interval", "label": "Intervall (s)", "type": "int", "default": 60}, ] - default_config = {"api_key": "", "interval": 60, "show_extra": True} - + default_config = {"api_key": "", "interval": 60} + def fetch(self): - # Daten holen (API call, etc.) return {"value": 42} - + def render(self, draw, fonts, x, y, w, h): - # In den Slot zeichnen d = self.fetch() - draw.text((x + 10, y + 10), f"Value: {d['value']}", font=fonts["28"], fill=FG) + # w, h sind Pixel (nicht Zellen!) + draw.text((x + 10, y + 10), f"Value: {d['value']}", + font=fonts["28"], fill=FG) ``` -**Feld-Typen**: `string`, `int`, `float`, `bool`, `secret` (write-only), `select` (mit `choices`), `lat_lon`. +**Palette:** `BLACK, WHITE, RED, GREEN, BLUE, YELLOW, ORANGE` + semantisch `FG, BG, OK, WARN, ALERT, INFO, ACCENT` -**Palette** (aus `palette.py`): `BLACK, WHITE, GREEN, BLUE, RED, YELLOW, ORANGE` + semantisch `FG, BG, OK, WARN, ALERT, INFO, ACCENT`. +## Services -## Mitgelieferte Plugins +```bash +systemctl --user enable --now epaper-dashboard +systemctl --user enable --now epaper-admin +journalctl --user -u epaper-dashboard -f # Logs +``` -- `clock` — Uhrzeit / Datum -- `weather` — Open-Meteo Wetter (kein API-Key) -- `system` — Pi CPU/RAM/Uptime -- `hello` — Demo -- `spotify` — Last.fm Scrobble -- `strava` — Strava Aktivitäten -- `gmail` — Gmail Unread +**Wichtig:** `PrivateTmp=no` in den Service-Files — sonst sieht der Admin-Socket `/tmp/epaper-dashboard.sock` nicht. -## Einschränkungen +## Display-Einschränkungen -- **Full Refresh only**: ACeP-Displays vertragen keine schnellen Partial-Refreshes (Verschleiß). Das Hersteller-empfohlene Minimum ist 180s. -- **Kein Live-Websocket**: Snapshot wird per Refresh-Now-Button ausgelöst. Bei Bedarf ergänzbar. +- **Nur Full-Refresh:** ACeP-Displays vertragen keine schnellen Partial-Refreshes. Minimum 180s. +- **Refresh-Dauer:** Ein Full-Refresh dauert ~35-65s. Währenddessen ist das Display leer/flackernd. +- **Ghosting:** Leichtes Ghosting bei manchen Farben normal. Display nicht bei direktem Sonnenlicht ablesen. + +## Recovery + +Falls das konfigurierte WLAN nicht erreichbar ist: der Pi startet automatisch einen **Recovery AP**. + +- **SSID:** `epaper-recovery` +- **Passwort:** `recovery1234` +- **URL:** http://10.42.0.1:8080/ + +Dort kannst du ein neues WLAN konfigurieren. Details in `RECOVERY.md`. + +## Lizenz + +Apache 2.0