ki 4cef32b919 FIX-AUTH: /api/power GET ohne Auth (nur Status-Lesen)
api_power() ist GET-only und gibt nur den aktuellen Status
zurueck. Kein Auth noetig fuer reine Lese-Operationen.
Entfernt require_auth() von dieser Route.
2026-08-31 03:12:20 +04:00

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

sudo apt update && sudo apt install -y python3-pil python3-numpy python3-flask git
sudo raspi-config  # → Interface Options → SPI → Enable
sudo reboot

Setup

# 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):

{
  "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
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

# 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

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.

Dort kannst du ein neues WLAN konfigurieren. Details in RECOVERY.md.

Lizenz

Apache 2.0

S
Description
Plugin-based ePaper dashboard for 7.3" ACeP 7-Color display on Raspberry Pi 4
Readme
887 KiB
Languages
Python 71.6%
HTML 24.6%
JavaScript 3.8%