ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Web-App für den Vertretungsplan des Konrad-Adenauer-Gymnasiums Meckenheim.
Die App liest die öffentlichen Untis-Seiten der Schule (subst_001.htm, …) und zeigt
nur die Einträge der eigenen Klasse, in der Oberstufe die der gewählten Kurse
(über die Kürzel der Lehrkräfte). Sie lässt sich auf den Home-Bildschirm legen, öffnet
auch offline mit dem zuletzt geladenen Plan und meldet Änderungen per Benachrichtigung.
Quelle: immer monitor2020.meckenheim-kag.com/schueler/. Nur wenn dort die erste
Seite nicht kommt, nimmt die App als Notlösung die zweite Kopie der Schule
(vertretung.meckenheim-kag.com), ohne die beiden zu vergleichen; beim nächsten Abruf
wird wieder monitor2020 versucht. Jeden Wechsel (ausgefallen / wieder da) meldet sie nur
dem Betreiber über FEEDBACK_WEBHOOK_URL (sonst ALERT_WEBHOOK_URL); /api/health
nennt die aktive Notlösung unter notes (lib/scraper.ts, lib/source-switch.ts).
Adresse beim Öffnen (lib/address.ts): ?day=2026-11-02 (aus einer Benachrichtigung)
zeigt diesen Tag, ?klasse=7B (Link aus einem geteilten Plan) zeigt die Klasse – ohne
eigene Klasse wird sie zur eigenen, sonst nur angesehen –, ?einstellungen=teilen öffnet
die Einstellungen beim QR-Code. Die App liest das einmal und nimmt es dann aus der Adresse,
damit ein Neuladen nicht wieder darauf springt. Eine nur angesehene Klasse gilt bis die App
neu geöffnet wird (nach 30 Minuten im Hintergrund oder am nächsten Tag).
Benachrichtigungen bei kaputtem Upload: Verschwinden auf einmal (fast) alle Einträge
eines Tages, meldet der Push-Check das erst, wenn es 45 Minuten so bleibt
(lib/push/diff.ts), statt allen „Findet doch statt“ zu schicken.
Weitergeben: Unter /teilen (auch über die Einstellungen erreichbar) gibt es einen
QR-Code zur App, der sich als Aushang drucken lässt.
| Teil | Wo | Aufgabe |
|---|---|---|
| Plan lesen | lib/scraper.ts, lib/parser.ts, lib/cache.ts | Lädt alle Untis-Seiten, liest Tage, Einträge und Mitteilungen, prüft das Ergebnis auf Plausibilität. Ist die Schulseite nicht erreichbar, wird der zuletzt gelesene Plan mit einem Hinweis gezeigt. |
| Schnittstelle | app/api/substitution | Liefert den gelesenen Plan an die App. |
| Oberfläche | app/page.tsx, components/, hooks/ | Startseite mit Tagesauswahl, Einstellungen (Übersicht mit Unterseiten), Einführung. Die Versionsnummer steht in package.json und wird bei jeder Änderung angehoben (siehe AGENTS.md). Auf großen Bildschirmen (PC, Mac, iPad quer) eine eigene Ansicht mit allen Tagen nebeneinander (components/DesktopView.tsx); die Handy-Ansicht bleibt davon unberührt. |
| Offline und Updates | public/sw.js, components/PwaRegistrar.tsx | Der Service Worker öffnet die App sofort aus dem Speicher und lädt im Hintergrund die neue Version. Gibt es ein neues Deployment, lädt die App sich neu, sobald das nicht stört (direkt nach dem Öffnen oder beim Verlassen). |
| Ferien und Feiertage | lib/holidays.ts | NRW-Ferien und Feiertage, siehe Pflege. |
| Benachrichtigungen | lib/push/, app/api/push/ | Vergleicht den Plan mit dem letzten Stand und schickt Mitteilungen, siehe unten. |
| Überwachung | app/api/health, lib/alert.ts | Meldet Probleme, siehe Überwachung. |
Technik: Next.js 16 (App Router), React 19, Tailwind CSS 4, Cheerio, web-push,
Upstash Redis, Vercel (Hosting und Web Analytics).
Die App schickt eine Mitteilung aufs Handy, sobald sich der Plan für die gewählte Klasse
bzw. die gewählten Lehrkräfte ändert, auch wenn die App geschlossen ist. Dafür ruft
cron-job.org alle 10 Minuten POST /api/push/check auf; die App liest dann den Plan,
vergleicht ihn mit dem letzten Stand und schickt die Mitteilungen.
Was gemeldet wird
Einstellbar in der App
Vercel deployt automatisch bei jedem Push auf main. Jeder Push ist also sofort
ein Release für alle Nutzer.
Die Serverfunktionen laufen in Frankfurt (fra1, festgelegt in vercel.json): nah an
der Schulseite und an Redis, und so steht es in der Datenschutzerklärung. Wer die Region
ändert, muss auch die Datenschutzerklärung anpassen.
KV_REST_API_URL/KV_REST_API_TOKEN)
setzt Vercel dabei selbst.npx web-push generate-vapid-keys. In Vercel unter
Settings → Environment Variables VAPID_PUBLIC_KEY und VAPID_PRIVATE_KEY
eintragen. Die Schlüssel nicht mehr ändern, sonst müssen alle Nutzer die
Benachrichtigungen neu einschalten.CRON_SECRET in Vercel setzen: ein langes Zufallswort, z. B. openssl rand -hex 32.https://vertretung-kag.vercel.app/api/push/check mit der Methode POST und dem
Header Authorization: Bearer <CRON_SECRET> aufruft. Auch nachts laufen lassen: Die
App sendet dann nichts, merkt sich aber, dass die Prüfung läuft.Ersatz-Zeitplan über GitHub: .github/workflows/push-check.yml ruft die Prüfung
ebenfalls auf (werktags alle 10 Minuten, am Wochenende alle 30), wenn in GitHub unter
Settings → Secrets and variables → Actions die Variable APP_URL und das Secret
CRON_SECRET gesetzt sind. GitHub startet solche Läufe oft verspätet; neben
cron-job.org ist er nicht nötig. Doppelte Aufrufe schaden nicht, die Prüfung ist
gegen gleichzeitige Läufe gesperrt.
| Variable | Zweck |
|---|---|
VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY | Pflicht für Benachrichtigungen. |
VAPID_SUBJECT | Optional, z. B. mailto:[email protected]. Standard: die Adresse der App. |
CRON_SECRET | Pflicht für Benachrichtigungen; schützt /api/push/check. |
KV_REST_API_URL, KV_REST_API_TOKEN (oder UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN) | Redis; setzt Vercel beim Verbinden selbst. |
ALERT_WEBHOOK_URL | Optional. Bekommt eine Nachricht, wenn der Plan nicht mehr vollständig gelesen werden kann (z. B. weil Untis das Format geändert hat) und wenn es wieder geht, außerdem wenn Benachrichtigungen bei keinem Gerät ankommen. Geht mit einem ntfy.sh-Thema (Push aufs Handy ohne Konto, z. B. https://ntfy.sh/ein-geheimer-name), einem Discord- oder Slack-Webhook. |
FEEDBACK_WEBHOOK_URL | Optional. Bekommt die Nachrichten aus Einstellungen → Hilfe & Kontakt (mit Geräte-Angaben, wenn der Nutzer sie nicht abwählt). Am besten ein geheimes ntfy.sh-Thema mit langem Zufallsnamen (wie ein Passwort; nur hier und in der eigenen ntfy-App eintragen). Ohne diesen Wert wird ALERT_WEBHOOK_URL benutzt; fehlen beide, bietet die App nur E-Mail an. Die Adresse bleibt auf dem Server. |
NEXT_PUBLIC_IMPRESSUM_URL, NEXT_PUBLIC_DATENSCHUTZ_URL | Optional. Andere Adressen für Impressum und Datenschutz. Standard: die Seiten auf support-vertretung-kag.vercel.app. Nach dem Ändern neu deployen. |
SUBSTITUTION_BASE_URL | Nur für Tests: andere Quelle für die Untis-Seiten. Standard: die Seite der Schule. |
PUSH_STORE=memory | Nur für Tests: Anmeldungen im Arbeitsspeicher statt in Redis. |
Geheime Werte (Schlüssel, CRON_SECRET, Webhook-Adressen) stehen nur in Vercel,
GitHub-Secrets und cron-job.org, niemals im Repository.
proxy.ts, lib/csp.ts): Nur die Skripte der App laufen, eingeschleuster
Code nicht. Dazu kommen die üblichen Schutz-Header aus next.config.ts.lib/legacy.ts).GET /api/health antwortet mit 200, solange der Plan vollständig gelesen wird
und der Push-Check in den letzten 30 Minuten lief, sonst mit 503 und den Gründen
(problems). Ein halbes Jahr bevor die eingetragenen Ferien enden (lib/holidays.ts),
meldet sie das ebenfalls als Problem. Ein kostenloser Uptime-Dienst (z. B. UptimeRobot) kann diese Adresse
alle paar Minuten abfragen und meldet sich, wenn etwas nicht stimmt, auch wenn die
Schulseite ganz ausfällt oder cron-job.org nicht mehr aufruft.ALERT_WEBHOOK_URL (siehe oben) meldet Lesefehler und Zustellprobleme von selbst..github/workflows/live-check.yml liest montags bis freitags früh den
echten Plan. Schlägt das fehl (auch beim zweiten Versuch zehn Minuten später),
schickt GitHub eine E-Mail. Von Hand: GitHub → Actions → Live-Check →
Run workflow. GitHub pausiert geplante Abläufe nach 60 Tagen ohne Push; dann dort
wieder aktivieren./app (vom Home-Bildschirm geöffnet) bzw.
/browser.npm install
npm run dev # http://localhost:3000
Vor jedem Commit muss alles grün sein:
npx tsc --noEmit -p . # Typen
npm run lint # ESLint
npm test # Unit-Tests (Vitest, Ordner tests/)
npm run build # Produktions-Build
npm run test:e2e # Browser-Tests (Playwright, Ordner e2e/) gegen den Build
npm run test:e2e:container # dasselbe mit vorinstalliertem Chromium (z. B. Cloud-Container)
npm run check:live # liest den echten Plan der Schule und prüft ihn
Die Browser-Tests laufen gegen gespeicherte Untis-Seiten (tests/fixtures/), die
e2e/fixture-server.mjs ausliefert. So sehen sie immer denselben Plan. Die CI
(.github/workflows/ci.yml) führt bei jedem Push alles außer check:live aus.
Ausführliche Arbeitsregeln (auch für KI-Agenten): AGENTS.md.
lib/holidays.ts enthält die NRW-Ferien laut Ferienordnung bis
Ostern 2030 und muss vorher ergänzt werden. Gesetzliche Feiertage in NRW berechnet
die App selbst. Bewegliche Ferientage der Schule sind nicht enthalten. Liegt für
freie Tage noch kein Plan vor, zeigt die App eine Ferien- bzw. Feiertagskarte – am
ersten freien Tag erst ab 10 Uhr (vorher wie an einem normalen Samstag). In den
Schulferien bleibt die Ferienkarte auch dann, wenn der Plan danach schon online ist;
sie bietet ihn und den letzten Plan vor den Ferien zum Antippen an (gilt bis zum
nächsten Tag; ein Link aus einer Benachrichtigung öffnet den Plan direkt). Am letzten
freien Tag zeigt die App ab der Uhrzeit aus „Ab wann den nächsten Schultag zeigen?“ (Standard
16 Uhr) direkt den Plan für den ersten Schultag, wie an einem normalen Wochenende..github/dependabot.yml öffnet wöchentlich Update-Pull-Requests,
die die CI prüft. Für Sicherheits-Updates in GitHub unter Settings → Code security
„Dependabot alerts“ und „Dependabot security updates“ einschalten.VERSION in
public/sw.js erhöhen.Auf dem Server (Redis):
Auf dem Gerät: Klasse, Einstellungen, der zuletzt geladene Plan und welche Einträge
schon gesehen wurden, alles unter Schlüsseln mit kag:. „App zurücksetzen …“ in den
Einstellungen löscht sie.
Kommen neue Daten oder Dienste hinzu, muss die Datenschutzerklärung auf der Support-Website angepasst werden.