# epaper-dashboard Plugin-based dashboard for the **Waveshare 7.3" ACeP 7-Color e-Paper (F) HAT** on Raspberry Pi 4. Rendert regelmäßig (Standard 180s, oder konfigurierbar; im DeepSleep-Modus nur zu Weckzeiten) 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 ``` ┌──────────────────────────────────────────────┐ │ epaper-dashboard │ ├──────────────────┬───────────────────────────┤ │ dashboard.py │ admin.py │ │ (Renderer + │ (Web Admin UI :8080) │ │ DeepSleep) │ │ ├──────────────────┴───────────────────────────┤ │ layout.py · 4×4 Grid · Pack-Algorithmus │ ├───────────────────────────────────────────────┤ │ plugins/ (eines pro Widget) │ │ clock · clock_wordclock · weather · system │ │ spotify · strava · gmail · minimax · │ │ netatmo · hello │ ├───────────────────────────────────────────────┤ │ shared: palette.py · plugins/base.py │ │ (Farben, Formatter, Render-Helper) │ ├───────────────────────────────────────────────┤ │ network_watchdog.py │ │ (WiFi AP/Client Management) │ ├───────────────────────────────────────────────┤ │ waveshare_epd/ (Vendor-Treiber) │ │ epd7in3f.py │ └───────────────────────────────────────────────┘ ``` ### Shared Foundation (Single Source of Truth) - **`palette.py`** — 7-Farben ACeP-Palette (`BLACK, WHITE, RED, GREEN, BLUE, YELLOW, ORANGE`), semantische Aliase (`FG, BG, OK, WARN, ALERT, INFO, ACCENT`), deutsche Datums-Formatter (`de_weekday_long`, `de_weekday_short`, `de_date`, `de_weekday_date`, `de_time`), `temp_color()` / `co2_color()` / `batt_color()`, Wetter-Icon-Mapping (`wmo_to_icon`). - **`plugins/base.py`** — `Widget`-Basisklasse, Render-Helper (`draw_battery`, `draw_compass`, `draw_trend`, `draw_line_chart`, `draw_mini_bar`, `draw_weather_icon`, `draw_zzz_indicator`, `draw_deepsleep_indicator`), Layout-Utilities (`is_small`, `is_wide`, `is_tall`). Alle Plugins nutzen diese Helper — keine duplizierte Render-Logik. ## Quick Start ### Pi vorbereiten ```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, "power": { "deepsleep_enabled": false, "wake_times": ["07:00", "12:00", "18:00"], "cpu_throttle": false }, "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 - **Energie-Tab** — DeepSleep-Modus, Weckzeiten, CPU-Throttle, manuelles Display-Aufwecken - **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 | `format_24h`, `show_seconds`, `show_date`, `show_weekday`, `accent_color` | | `clock_wordclock` | Systemzeit | `show_date`, `show_weekday`, `show_es_ist`, `invert`, `accent_color` (QlockTwo-Style Wort-Uhr) | | `weather` | Open-Meteo (kein API-Key) | `location` (lat,lon), `show_uv`, `show_forecast_hours`, `show_wind_compass` | | `system` | Pi CPU/RAM/Uptime | `show_uptime`, `show_load`, `compact`, `bar_thresholds`, `bar_gradient` | | `spotify` | Last.fm Scrobble | `api_key`, `username` | | `strava` | Strava API | `client_id`, `client_secret`, `refresh_token` | | `gmail` | Gmail API | `oauth_token_json` | | `minimax` | MiniMax Token Plan | `subscription_key`, `show_credits`, `api_base`, `bar_thresholds`, `bar_gradient` | | `netatmo` | Netatmo Weather API (OAuth2) | siehe [plugins/NETATMO.md](plugins/NETATMO.md) | | `hello` | statisch | `text`, `color` | ## DeepSleep / Energie Das Dashboard unterstützt einen **DeepSleep-Modus** für stromsparenden 24/7-Betrieb: - **Weckzeiten** (z.B. `["07:00", "12:00", "18:00"]`) — Display wacht zu diesen Zeiten auf, rendert neu, schläft wieder. - **CPU-Throttle** (Pi auf 600MHz im Sleep) — spart ~1.5W am Raspberry Pi. - **Manuelles Wecken** — Button im Admin-UI unter `/power` triggert sofortigen Refresh. - **ZZZ-Indikator** — `draw_zzz_indicator` aus `plugins/base.py` zeigt im Sleep-Zustand dezent "zZz" oben links. - **60s-Sleep-Chunks** — Main-Loop schläft in 1-Minuten-Häppchen statt eines langen Intervalls, damit das Weck-Signal schnell greift. Konfiguration in der Admin-UI (`/power`) oder direkt in `config.json` unter `power.*`. Defaults werden beim ersten Start aus `POWER_DEFAULTS` in `palette.py` auto-gefüllt. ## Eigenes Plugin schreiben ```python # plugins/my_widget.py from plugins.base import Widget from palette import FG, BG, de_weekday_date class Widget(Widget): name = "my_widget" label = "Mein Widget" description = "Zeigt etwas" category = "info" config_schema = [ {"key": "api_key", "label": "API Key", "type": "secret"}, {"key": "interval", "label": "Intervall (s)", "type": "int", "default": 60}, ] default_config = {"api_key": "", "interval": 60} def fetch(self): return {"value": 42} def render(self, draw, fonts, x, y, w, h): d = self.fetch() # w, h sind Pixel (nicht Zellen!) # Nutze die shared helper aus palette + base draw.text((x + 10, y + 10), f"Value: {d['value']}", font=fonts["28"], fill=FG) ``` **Palette:** `BLACK, WHITE, RED, GREEN, BLUE, YELLOW, ORANGE` + semantisch `FG, BG, OK, WARN, ALERT, INFO, ACCENT` **Tipp:** Vermeide eigene `draw_compass`/`draw_weather_icon`/etc. — nutze die Helper aus `plugins/base.py`. Sonst dupliziert sich die Render-Logik und das Design driftet auseinander (siehe Commit-History FEAT-NETATMO-06). ## Services ```bash systemctl --user enable --now epaper-dashboard systemctl --user enable --now epaper-admin journalctl --user -u epaper-dashboard -f # Logs ``` **Wichtig:** `PrivateTmp=no` in den Service-Files — sonst sieht der Admin-Socket `/tmp/epaper-dashboard.sock` nicht. ## Display-Einschränkungen - **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