- Python 75.3%
- HTML 14.7%
- Nix 9%
- Makefile 1%
| firmware | ||
| server | ||
| tests | ||
| tools | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| config.example.json | ||
| flake.lock | ||
| flake.nix | ||
| Makefile | ||
| nc.env.example | ||
| nixos-module.nix | ||
| README.md | ||
| renderer.nix | ||
| requirements.txt | ||
Inkplate 10 Familien-Dashboard
Batteriebetriebenes E-Paper-Dashboard (Inkplate 10) an der Wand: zeigt Wetter, Nextcloud-Kalender und Nextcloud-Todos. Der Server rendert das Dashboard als PNG; das Inkplate zieht (nicht pusht) das Bild stündlich 06–20 Uhr und schläft dazwischen tief — das ist der Schlüssel zur Batterielaufzeit.
┌────────────────────────────┐ ┌───────────────────────────────┐
│ LAN-Render-Server (NixOS) │ │ Inkplate 10 │
│ Open-Meteo (Wetter) │ │ MicroPython │
│ Nextcloud CalDAV │ HTTP │ RTC-Wecker → Bild holen → │
│ (Kalender + Todos) │ GET │ zeichnen → Deep-Sleep │
│ → dashboard.png (1200×825)│ <---- │ (nur 06–20 Uhr, sonst schlaf)│
│ statischer Server :8080 │ ⚟─── 22 µA im Tiefschlaf │
└────────────────────────────┘ └───────────────────────────────┘
Push wäre falsch: der ESP32 müsste wach bleiben und die Batterie leert
sich. Pull = robuster UND batteriefreundlich.
Layout
flake.nix # devShell + Renderer + NixOS-Modul
renderer.nix # das Renderer-Paket (Python + Chromium + Fonts)
nixos-module.nix # systemd-Timer (06–20 Uhr) + statischer Server
config.example.json # Vorlage: Wetter-ORT, Nextcloud, Kalender-Filter
nc.env.example # Secrets-Vorlage (nie committen!)
firmware/main.py # MicroPython-Client für den Inkplate 10
server/inkplate_server/ # config, fetch, ics, render, serve, templates
Makefile # make render / make serve / make dev
1. Konfiguration
cp config.example.json config.json # URL + Wetter-Koordinaten anpassen
cp nc.env.example nc.env # NEXTCLOUD_USER / NEXTCLOUD_PASSWORD
- Wetter: Open-Meteo, kostenlos, kein API-Key (Koordinaten +
tz_name). Neben Temperatur und Wettercode nutzt das Dashboard die stündliche Niederschlagswahrscheinlichkeit für das stärkste verbleibende Regenfenster. - Nextcloud: App-Passwort! (Einstellungen → Sicherheit → App-Passwörter)
calendar_filter: Teilstrings gegen Kalendername/-ID, z.B.["familie", "todo"]. Leer = alle. Dein Setup:familie_shared_by_harald→ "Familie",todo_shared_by_harald→ "Todo - Harald und Meike".- Secrets kommen optional aus der Umgebung:
NEXTCLOUD_USER/NEXTCLOUD_PASSWORD(Alias fürNEXTCLOUD_USERNAME). - Ein laufender ganztägiger Termin, dessen Titel auf
ferienendet (Sommerferien,Pfingstferien, …), ersetzt den Stundenplan automatisch durch einen Countdown bis zum exklusivenDTEND/ersten Schultag.
2. Lokal rendern (auf dem Mac)
make setup # venv mit jinja2 + tzdata
# Chromium/Chrome nötig — auf macOS am einfachsten:
export CHROMIUM_BIN="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
make render # → build/dashboard.png (1200×825)
make serve # liefert es auf 0.0.0.0:8080 aus
open build/dashboard.png # Ergebnis ansehen
Renderer-Kern: python -m inkplate_server.render --config config.json --out build/dashboard.png.
3. NixOS-Server (später)
# configuration.nix
{
inputs.inkplate.url = "github:Harald/inkplate"; # oder lokaler Pfad
config.services.inkplate = {
enable = true;
configFile = "/etc/inkplate/config.json";
secretsFile = "/etc/inkplate/nc.env"; # NEXTCLOUD_USER/NEXTCLOUD_PASSWORD
schedule = [ "*-*-* 06..20:00:00" ]; # stündlich 06–20 Uhr
};
}
systemd-Timer rendert stündlich; ein statischer Dateiserver liefertdashboard.pngauf0.0.0.0:8080aus (Port vialistenPort).- Ausgabeverzeichnis:
/var/lib/inkplate(viaStateDirectory). - Chromium braucht Fonts — das Paket setzt eine fontconfig mit DejaVu/Liberation; für eigene Glyphen dort Fonts ergänzen.
4. Firmware (Inkplate 10)
- MicroPython flashen: Inkplate 10 braucht das MikroPython mit dem
Inkplate-Modul (Anleitung:
docs.soldered.com/inkplate/10→ MicroPython → Setup). firmware/main.pyanpassen:SSID,PASSWORD,SERVER(IP des LAN-Servers).- Als
main.pyauf den ESP32 laden (SD-Karte oder Soldered-Tool). Nach jedem Wake läuftmain.pyneu — das Skript entscheidet selbst, ob es ein Update-Zeitpunkt ist (06–20 Uhr), und schläft sonst bis zum nächsten Slot.
Für den direkten Upload am Mac gibt es einen robusten Helfer, weil ein normales
mpremote cp das sofort wieder schlafende Gerät leicht verpasst:
nix develop --command make firmware-install
Er erkennt den USB-UART automatisch und hält ihn während Hardware-Reset,
REPL-Abfangen, API-Probe, atomarem main.py-Upload und Neustart durchgehend
offen. Sind mehrere passende Boards angeschlossen, kann der Port mit
PORT=/dev/cu.usbserial-210 festgelegt werden.
Beim Bildabruf hängt die Firmware die mit read_battery() (ältere Bibliothek;
Fallback readBattery()) gemessene Spannung als Millivolt an die URL
(dashboard.png?battery_mv=3870&format=.png). Der scheinbar redundante
format-Wert ist nötig, weil der Inkplate-Treiber das Bildformat am Ende der
gesamten URL erkennt. Der Server rendert daraus ein vierstufiges Batteriesymbol;
eine fehlende Messung verhindert den Bildabruf nicht.
Der linke Gehäusetaster ist der aktive-low Wake-Taster auf GPIO36. Die Firmware registriert ihn als externe Deep-Sleep-Weckquelle und zeigt nach dem Aufwachen lokal ein kleines Kästchen mit drei Punkten in der Bildschirmmitte. Ein kurzer Druck blättert in der Reihenfolge Dashboard → Wetter → Details; die gewählte Seite bleibt über den Deep-Sleep erhalten. Ein weiterer Druck, nachdem das Kästchen sichtbar ist, öffnet die Quatschseite. Dort lädt jeder kurze Druck einen weiteren Gag aus dem vorgerenderten Vorrat und startet das 30-sekündige Bedienfenster erst nach dem fertigen Display-Refresh neu. Langes Halten beendet das Karussell sofort; nach fünf Minuten endet es spätestens. Wird der Knopf beim Aufwachen lange gehalten, erscheint für 30 Sekunden die Technikseite. Beide Sonderseiten kehren danach zur gewählten normalen Seite zurück. Timer-Wake und Power/Reset beginnen wieder beim Dashboard.
Der Timer inkplate-gags lässt das lokale LLM morgens standardmäßig eine
Mischung aus zwölf Quatschmeldungen, Bewegungsaktionen, Zungenbrechern,
Geheimaufträgen, Scherzfragen und einer freien Überraschung erzeugen und
rendert alle PNGs vor. Der Tastendruck
wartet dadurch weder auf das Modell noch auf Chromium. Misslingt das Vorwärmen,
bleibt der vorige Vorrat erhalten; ohne Vorrat greift die eingebaute Liste.
Jeder neue Lauf bekommt einen zufälligen Seed und die Texte des vorigen Pools
als Negativbeispiele, damit nicht täglich dieselben Ideen wiederkommen.
Stündliche Timer-Wakes überspringen die Sonderseiten vollständig.
Für das Rückmelde-Kästchen wechselt die Firmware kurz in den 1-Bit-Modus und zeichnet nur Rahmen und Punkte per Partial-Update über das bestehende Bild. Danach geht es für die Serverbilder zurück in den 2-Bit-Graustufenmodus. Auf dem echten Gerät wurden 214 ms reine Zeichenzeit beziehungsweise 588 ms für die gesamte Display-Sequenz gemessen.
Wichtige API (verifiziert): from inkplate10 import Inkplate,
Inkplate(Inkplate.INKPLATE_2BIT), inkplate.begin(),
inkplate.draw_image(url, 0, 0, dither=True, kernel_type=Inkplate.KERNEL_FLOYD_STEINBERG),
inkplate.display(), inkplate.rtc_get_data() → dict.
5. Powerbank-Gotcha (du hattest eine alte Powerbank angekündigt)
Problem: Powerbanks mit Auto-Off schalten ab, weil das E-Paper im Schlaf nur ~22 µA zieht. Lösungen, einfach bis aufwendig:
- Eine Powerbank ohne Auto-Off / mit Konstantstrom-Modus verwenden.
- Kleinen Dauer-Verbrauch: ein Schalter-Widerstand/LED-Dummy in den USB (ca. 50 mA).
- Den JST-Eingang des Inkplate direkt mit einem LiPo versorgen (MCP73831-Lader ist schon auf dem Board) — dann entfällt die Powerbank-Fehlerei ganz.
Einschränkungen
- Kalender-Recurrence: nur
DAILY/WEEKLY(mitBYDAYundINTERVAL), begrenzt durchUNTIL/COUNT.MONTHLY/YEARLY/RDATEwerden ignoriert (für v1 ok). Verschobene und abgesagte Einzeltermine einer Serie (RECURRENCE-ID) werden berücksichtigt. - Vom Server geholt wird nur das Nötige: Termine per CalDAV-
time-rangeim Anzeigefenster, Todos ohneCOMPLETED— sonst lädt jeder Render den kompletten Kalenderverlauf (1,1 MB). - Zeitzonen:
TZIDund UTC (Z) werden korrekt in lokale Zeit umgerechnet. - NTP-Sync einmal täglich; die Umstellung zwischen MEZ und MESZ berechnet die Firmware nach der EU-Regel automatisch.
- PNG maximal ~800 kB — unser graustufenbasiertes Dashboard ist weit darunter.