ESP32-S3 Bench Guide
ESP32‑S3 N16R8 벤치 가이드: 실시간 핀아웃, 강의, 시뮬레이터 및 벤치 로그
Actually Usable Calendar: 오프라인 우선 WebUntis 시간표 앱. 무료 APK 다운로드 또는 브라우저 사용
Actually Usable Calendar — a fast, offline-first WebUntis timetable app that actually works. Free APK download, or use it right in the browser.
교육TypeScript · HTMLGitHub @Nils-dev-mertens온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Just a better fucking untis app
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A school timetable app for AP Hogeschool students: fetches lessons directly from the WebUntis REST API (the same source the AP-WebUntisToICS-Node server uses), stores everything offline, and stays out of your way.
• React Native + Expo SDK 57 (expo-router), bun
• React Native Reusables (RNR) components + NativeWind v4, dark theme
• Syncs once, then works fully offline (AsyncStorage)
• No server needed on mobile — Android/iOS call WebUntis directlyREADME 전문 보기 ↗A school timetable app for AP Hogeschool students: fetches lessons directly from the WebUntis REST API (the same source the AP-WebUntisToICS-Node server uses), stores everything offline, and stays out of your way.
bun install
bun start # dev server (scan QR for Expo Go / dev build)
The first launch walks you through: WebUntis server → school year → class.
bun run web:build # expo export --platform web → dist/
bun run web:serve # serves dist/ on http://localhost:8080
Or run the web preview in Docker (no local bun/node needed):
docker build -f Dockerfile.web -t calendar-web .
docker run -d -p 8080:8080 calendar-web # → http://localhost:8080
Browsers require CORS preflight approval for the API's custom
anonymous-school header, which WebUntis does not grant. The web build
therefore goes through the same-origin proxy at /proxy?url=<encoded>
(which forwards that header) served by web:serve. Running under bun start
alone has no proxy — you'll get HTML instead of JSON.
Android/iOS are unaffected and hit the API directly.
Base URL (the API_BASE_URL from the reference project's .env.example):
https://ap.webuntis.com/WebUntis/api/rest/view/v1
The school tenant is selected with the anonymous-school: ap request header.
Responses are cached in memory with the reference server's TTLs: school
years / class lists for 7 days, timetable entries for 15 minutes.
| Endpoint | Used for |
|---|---|
GET /schoolyears | available school years |
GET /app/data | current school year (default date range) |
GET /timetable/filter?resourceType=CLASS&start=&end= | class list for a year |
GET /timetable/entries?resourceType=CLASS&start=&end=&resources=<id> | timetable entries |
Lessons are mapped from the raw grid entries (positions 1–7 → subject / teachers / rooms / info / classes) the same way the reference server does, including merging same-slot lessons and subject filtering (hidden subjects are excluded at sync time).
The app is distributed as a self-hosted APK: the landing page (website/index.html, plain HTML, no build step) offers the download; install once, and receive JS-only fixes silently via EAS Update
(no reinstall). Native changes require a new APK; the fingerprint
runtime-version policy guarantees devices only get compatible updates.
The landing page resolves the newest release at runtime via the GitHub API, so its download button always points at the latest APK without redeploying. Host it anywhere static files work — e.g. with the bundled Dockerfile:
docker build -t betteruntis-site .
docker run -d -p 8080:80 betteruntis-site # → http://localhost:8080
bun run site:serve # local preview without Docker
Both containers together (landing page on :5200, web preview on :5201):
docker compose up -d --build
| Pipeline | Trigger | Output |
|---|---|---|
deploy.yml | push to main | Rebuilds and redeploys both web containers on the own server via docker compose (landing page :5200, web preview :5201) |
update.yml | push to main | EAS OTA update (JS-only, typechecked first) |
release.yml | tag v*.*.* | APK built on EAS cloud, attached to a GitHub Release (= download button target) |
release.yml also carries a version failsafe: it rewrites app.json,
package.json and android.versionCode from the pushed tag (e.g. tag
v1.0.2 → version 1.0.2, versionCode 10002), commits that back to
main, and only then builds — so a release can never ship with a stale
version number or a versionCode too low to install over the previous APK.
fingerprint.config.js keeps version fields out of the update fingerprint,
so those bumps don't break in-app OTA updates for older installs.
Both need an EXPO_TOKEN repo secret (create at expo.dev/settings/access-tokens).
Manual equivalents:
eas update --auto --branch production # push an OTA update
eas build -p android --profile apk # build APK on EAS cloud (same as the release pipeline)
In-app, Settings → "Check for updates" fetches and applies OTA updates immediately; otherwise they apply on the next launch.
bun run typecheck # typecheck app + server
bunx expo run:android # native build
src/app/ — routes: setup wizard, calendar, settingssrc/components/ — week grid (3/5/7-day), agenda, now banner, lesson & add-lesson sheetssrc/lib/ — data layer: WebUntis client + mappers (webuntis.ts), sync, storage, ICS/CSV export, hidden-rule logicsrc/context/calendar-context.tsx — app state, repeat expansion, persistencesrc/components/ui/ — RNR primitivesserver/ — static web server with the CORS proxy (web only; mobile needs no server)