Easy-to-manage touch screens
무료 오픈소스 터치스크린으로 USB 설정 가이드, 드래그‑드롭 편집기, 로컬 제어를 제공하며 Home Assistant용으로 제작되었습니다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Self-hosted family dashboard for kitchen wall screens, phones and desktops. See today at a glance: each person’s routines, tasks, calendar and optional rewards. Picture-based routines young kids can follow, German & English, CalDAV sync, Next.js + PostgreSQL, runs in Docker.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A self-hosted family dashboard for the kitchen wall, built around the family's day rather than the smart home:
Kindo puts one screen on the wall that the whole family can read at a glance: what's happening today, and what each person still has to do. Young children who can't read yet follow their morning and evening routines through large picture cards. Parents get the same data on their phones.README 전문 보기 ↗A self-hosted family dashboard for the kitchen wall, built around the family's day rather than the smart home:
People → Today → Routines & Tasks → Calendar → Rewards
Kindo puts one screen on the wall that the whole family can read at a glance: what's happening today, and what each person still has to do. Young children who can't read yet follow their morning and evening routines through large picture cards. Parents get the same data on their phones.
Status: v0.2. Roadmap steps 1–12 are in (see
docs/SPEC.md, Roadmap): PostgreSQL with live sync between screens, the recurrence engine, logins with single sign-on and paired wall displays, Nextcloud/CalDAV, Immich, ICS, Google Calendar, Home Assistant presence and Home control (a few switches and the solar flow), Frigate cameras with the doorbell and talking back, a kids' shelf of music and audiobooks from Jellyfin and Audiobookshelf (on the screen or a Home Assistant speaker), talking to Home Assistant with a held button, and an installable PWA with an offline shopping list. A new install starts empty; the Müller demo family can be loaded on the first-run screen.
| Wall display | Child view |
|---|---|
| Home (desktop) | Calendar (dark) |
The website in website/ (overview, technical data and setup guide, live demo; English and German) is published to GitHub Pages by .github/workflows/pages.yml, together with a live demo that runs in your browser: the real app with the Müller family kept on your device, nothing to sign in to (see demo/). Enable it once under Settings → Pages → Source: GitHub Actions.
To try the complete Kindo with the Müller demo family, integrations and logins included, no .env needed:
docker compose -f docker-compose.demo.yml up -d # http://localhost:3000, [email protected] / kindo-demo
docker compose -f docker-compose.demo.yml down # stop and forget everything
The demo keeps its database in memory and loads the family afresh every hour (KINDO_DEMO_RESET_MINUTES, 0 = never). It uses a public secret key: never keep real data in it.
Requirements: Docker with Compose v2.
cp .env.example .env # set POSTGRES_PASSWORD and KINDO_SECRET_KEY; optional: APP_URL, single sign-on, time zone
docker compose up -d
Open http://localhost:3000 and set up your household and your admin login, or tick Start with the demo family.
OIDC_ISSUER, OIDC_CLIENT_ID and OIDC_CLIENT_SECRET, with the redirect URI <APP_URL>/api/auth/callback/oidc. It signs in people whose email already has a login; it never creates new ones. Set KINDO_PASSWORD_LOGIN=false to make single sign-on the only way in (ignored while single sign-on isn't configured)./pair on the tablet. It shows a code and a QR code; an admin confirms it in Settings → Devices. The tablet then shows the wall and the child view without a login. It can tick off routines, chores and shopping; anything else (approving extras, planning, settings) asks for the settings PIN, which an admin sets in Settings → Devices and which unlocks the tablet for 10 minutes. The PIN never gives admin rights: people, logins, devices and integrations need an admin's own login./wall (or /screensaver); both ask the browser to keep the screen on. The browser only allows that over https (or localhost) and from Chrome / Android System WebView 84 on; otherwise it silently refuses and Android's timeout wins. On the tablet:
/wall, Keep screen on on, its own screensaver and screen-off timer off (so Kindo's photo frame shows), autoplay on for the doorbell's sound. Keep updated if the Play Store still offers it.| Service | What it does |
|---|---|
db | PostgreSQL 17. Data in ./data/postgres (POSTGRES_DATA_PATH). Not published outside the compose network. |
migrate | One-shot: applies database migrations (ghcr.io/macnite/kindo-migrate), then exits. With KINDO_DEMO=true it loads the demo family into an empty database. |
app | The Next.js server (ghcr.io/macnite/kindo). Waits for migrate. Healthcheck at /api/health (includes a database round-trip). Photo cache in the kindo-cache volume. |
Back up ./data/postgres (or run docker compose exec db pg_dump -U kindo kindo > kindo.sql).
docker compose up -d --build
The Publish image workflow (.github/workflows/publish.yml) first runs the whole CI workflow for that commit (lint, typecheck, unit, integration and end-to-end tests, the image smoke test) and pushes to GitHub Container Registry only if it passes.
| Trigger | Tags | Platforms |
|---|---|---|
Push to main | latest, main, sha-<short> | linux/amd64 |
Tag v1.2.3 | 1.2.3, 1.2, 1, latest, sha-<short> | linux/amd64, linux/arm64 |
Tag v0.x.y | as above, without the bare major tag | linux/amd64, linux/arm64 |
| Manual run | as for the ref | selectable |
Images carry provenance attestations and an SBOM. The migration image ghcr.io/macnite/kindo-migrate is published alongside with the same tags. For a reproducible deployment, pin the same version on both in .env:
APP_IMAGE=ghcr.io/macnite/kindo:0.2.0
MIGRATE_IMAGE=ghcr.io/macnite/kindo-migrate:0.2.0
To release, tag and push: git tag v0.2.1 && git push origin v0.2.1 (bump version in package.json first).
After the first publish, GHCR packages are private by default. To pull without logging in, open the package on GitHub → Package settings → Change visibility → Public.
Requirements: Node.js 22 or newer (the image runs 26, CI's tests run on 22) and a PostgreSQL you can create databases in.
npm install
cp .env.example .env # then set DATABASE_URL (e.g. postgresql://kindo:kindo@localhost:5432/kindo)
# and KINDO_SECRET_KEY: `npm start` runs in production mode and refuses to start without it
npm run db:migrate # apply migrations
npm run db:seed:demo # optional: the Müller demo family
npm run dev # http://localhost:3000
| Command | What it does |
|---|---|
npm run dev | Development server |
npm run build / npm start | Production build, served from the standalone output exactly as in the image |
npm run lint | ESLint (next/core-web-vitals, next/typescript) |
npm run typecheck | tsc --noEmit |
npm test | Vitest: unit tests, plus integration tests against TEST_DATABASE_URL when it is set, and against a real CalDAV server when CALDAV_TEST_URL is set (skipped otherwise) |
npm run test:e2e | Playwright. Creates, migrates and seeds its own database (<name>_e2e next to DATABASE_URL), then starts the production server |
npm run check | lint + typecheck + tests |
npm run db:migrate / db:migrate:dev | Apply migrations / create a new one after changing prisma/schema.prisma |
npm run db:drift | Fails when the schema and the committed migrations disagree (needs SHADOW_DATABASE_URL) |
npm run build:demo | Build the static GitHub Pages demo into demo/out (see demo/README.md) |
npm run db:seed:demo | Load the demo family into an empty database (-- --reset wipes a demo household and loads it afresh) |
CI (.github/workflows/ci.yml) runs all of the above against PostgreSQL on every push. It also builds both images, migrates a fresh database with the migrate image, and checks that the app starts, becomes healthy, carries no Prisma CLI and runs as a non-root user.
| URL | Screen |
|---|---|
/ | Home: family lanes and widgets (desktop), separate layout on phones |
/wall | Wall display (kiosk) with photo frame |
/kids, /kids/lena, /kids/paul | Child view |
/calendar, /routines, /tasks, /shopping, /meals, /rewards, /photos, /settings | Management screens |
/home-control, /cameras | Home control and cameras, once set up |
/screensaver | The photo frame on its own |
src/
app/
(app)/ Management screens inside the AppShell (rail on desktop, bottom nav on phones)
(kiosk)/ Full-screen surfaces: wall, kids, screensaver
setup/ First-run setup
login/ pair/ Sign-in, and pairing a wall display
offline/ Shown by the service worker when a page isn't cached
api/health/ Liveness endpoint for Docker (with a database round-trip)
api/stream/ Server-Sent Events: tells every screen when something changed
api/auth/ Better Auth (password and single sign-on)
api/photos/ Immich photo proxy with the disk cache
api/cameras/ Camera still pictures and the WebRTC handshake (Frigate)
api/media/ Kids' shelf: sound and covers through Kindo, signed addresses for speakers
api/assist/ Push-to-talk: a short recording to Home Assistant's Assist
api/integrations/ Google Calendar OAuth start and callback
layout.tsx, providers.tsx, manifest.ts, sw.ts
components/
ui/ Design-system primitives (Avatar, Button, Panel, Dialog, Pictogram, …)
layout/ AppShell, navigation, language/theme toggle
widgets/ FamilyLanes, dashboard widgets, HomeScreen, WallDashboard
routines/ ChildRoutine, RoutinesScreen, RecurrenceEditor, PictogramPicker
auth/ setup/ Login and pairing screens, first-run setup
home/ cameras/ Home control and talking, cameras and the doorbell
media/ The kids' shelf, the player and the wall tile
birthdays/ The birthday wheel
calendar/ shopping/ rewards/ photos/ settings/
lib/
types.ts Domain model: the contract between UI and server
services/ Selectors over the household snapshot, and the Server Action seam (actions.ts)
state/ Device prefs and the household store (snapshot, optimistic updates, live refresh)
recurrence.ts Recurrence model, matcher, RRULE mapping
ledger.ts The reward rule (§9)
pictograms.tsx Pictogram library
server/
household.ts The household's rules and writes (plain functions over Prisma)
actions/ Server Actions: thin wrappers that parse input and announce changes
snapshot.ts Loads everything a screen needs in one round-trip
realtime.ts PostgreSQL LISTEN/NOTIFY → SSE
jobs.ts Background jobs, run by the instance holding an advisory lock
calendar/ CalDAV, Google and ICS sync
contacts/ CardDAV birthdays
photos/ Immich sync, playlist and the photo cache
homeassistant.ts Home Assistant: presence, switches, doorbell sensors
home.ts Home control (§21)
cameras.ts Cameras and talking back (§22); frigate.ts talks to Frigate
media/ The kids' shelf (§23): Jellyfin, Audiobookshelf, speakers
assist.ts Talking to Home Assistant (§24)
demo/ The Müller demo family and the seed
i18n/ messages/en.ts + de.ts (parity enforced by types and tests), formats
instrumentation.ts Starts the background jobs when the server starts
demo/ The static GitHub Pages demo: Server Actions swapped for an in-browser household (§20 D74)
prisma/ schema.prisma, migrations, seed-demo.ts
scripts/ start-standalone.mjs, check-migration-drift.mjs, make-icons.mjs, build-demo.mjs
tests/ Integration tests against PostgreSQL
e2e/ Playwright specs, mock servers (OIDC, Immich, Home Assistant, Frigate, Jellyfin and Audiobookshelf), and prepare-db.ts for the suite's own database
docker/ entrypoint.sh, migrate.sh, healthcheck.sh
docs/ SPEC.md (product + decisions), screenshots
UI strings live in src/i18n/messages/. de.ts is typed against en.ts, so a missing German string fails the build. A unit test also checks that placeholders match. Language (UI text) and region (date format, 12/24 h, week start, numbers, currency) are set separately in Settings → Language & region.
Everything below runs on the server. Passwords, API keys and tokens are entered in Settings → Integrations, stored encrypted with KINDO_SECRET_KEY, and never sent to a device.
https://cloud.example.com/remote.php/dav), username and an app password. Kindo discovers the calendars, and in Settings → Calendar you choose whose each one is (its events take their colours), whether it is read-only, and whether it is daily attendance like school. Calendars are synced every few minutes (KINDO_SYNC_MINUTES) into Kindo's database, so the wall stays fast when Nextcloud is down. New events and changes to single events are written to Nextcloud first; the people Kindo assigns travel along in X-KINDO-MEMBERS. Repeating series are shown, and edited in the calendar app they come from.album.read, asset.read, asset.view) in Settings → Integrations. Its albums appear on the Photos screen; tick the ones for the photo frame and set how often each appears. Album lists refresh every half hour. Images reach devices only through Kindo (/api/photos/…), cached on disk in the kindo-cache volume (KINDO_PHOTO_CACHE_MB, default 500 MB, least recently shown go first). If photos stay blank, docker compose logs app | grep photo names the cause (Immich's answer, a network error, or a cache directory the app can't write; photos are still served then, just not cached).webcal:// works too), choose whose it is and whether it's daily attendance. Read-only, refreshed every half hour. The address is stored encrypted, since private feeds work like a password.<APP_URL>/api/integrations/google/callback, Calendar API enabled) and set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET. Then Settings → Integrations → Google signs an account in; its calendars sync like Nextcloud's, and writable ones accept new events and edits of single events.Every connection has a cog: change its address, account or secret in place (an empty secret keeps the stored one), and open its own setup.
Kindo shows Frigate's cameras as still pictures on the wall (the Cameras tile in Settings → Dashboard), as a home-screen widget and on /cameras; a tap opens one live, with sound. A doorbell press, reported by Home Assistant, opens the doorbell's camera full screen on every screen, over the photo frame too. Adults, or a wall display unlocked with the PIN, can hold Talk to speak through the doorbell. Kindo never offers to unlock a door.
First make it work in Frigate itself, from the tablet that will hang on the wall: live view with sound, and two-way talk in Frigate's own UI. If Frigate can't talk to the doorbell, Kindo can't either. For a Reolink doorbell, Frigate's docs (Camera specific → Reolink, Live view → Two way talk, Restream → Preventing go2rtc from blocking two-way audio) suggest:
go2rtc:
streams:
front_door: # watching, recording, detection
- "ffmpeg:http://DOORBELL_IP/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=USER&password=PASSWORD#video=copy#audio=copy#audio=opus"
front_door_twt: # talking: the bare rtsp:// source carries the speaker, so no # options on it
- "ffmpeg:http://DOORBELL_IP/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=USER&password=PASSWORD#video=copy#audio=copy#audio=opus"
- "rtsp://USER:PASSWORD@DOORBELL_IP:554/Preview_01_sub"
webrtc:
candidates:
- FRIGATE_LAN_IP:8555 # the address the wall tablet reaches Frigate at
Any other rtsp:// source of the same camera that recording or detection uses needs #backchannel=0, or go2rtc keeps the speaker for itself. Check the exact source URLs against Frigate's current docs for your model and firmware.
Then in Kindo:
https://nvr.local:30193). Use a Frigate user just for Kindo. Frigate's default certificate is self-signed: turn on Trust Frigate's own certificate and Kindo pins exactly that certificate (its SHA-256 fingerprint is shown in the camera setup); any other certificate at that address is refused before the password is sent.front_door, front_door_twt) and pick the doorbell's Visitor sensor from Home Assistant (Reolink: binary_sensor.<name>_visitor).Network and browser:
localhost): open Kindo over https with a certificate the tablet trusts to talk. Watching and the ring work over http too.https://frigate.example.com): Kindo's server must reach that name itself. Kindo's container resolves it with its own DNS, which from inside the house often points to the public address and fails without NAT loopback; then use Frigate's LAN address and port 8971 instead (with Trust Frigate's own certificate). A sign-in proxy (authentik, Authelia) in front of Frigate redirects Kindo to its login page: let /api through, or use port 8971 directly. When connecting fails, the dialog shows the reason.AGPL-3.0-only. Kindo was inspired by the feature sets of FamilyHub and Kinboard but shares no code, names or assets with them.
/wall. If the screen stays on and the photos appear after Kindo's idle time, it works; then set the timeout back.KINDO_TRUST_PROXY=true so sign-in attempts are limited per client address too. Without one, leave it off: anyone could send an X-Forwarded-For header./home-controlAPP_URL, so set it to an address the speakers reach; Google Cast devices often ask Google's DNS rather than yours, so give them an IP address or a name public DNS knows, and plain http on the home network or a trusted certificate). Google Cast speakers, Nest speakers and Chromecasts come in through Home Assistant's Google Cast integration; Kindo plays the next file on them itself, since they can't queue, and turns on Talk to Home Assistant: adults, or a wall unlocked with the PIN, hold a button on Home control and speak; Home Assistant's Assist does the rest. What a command may switch is Home Assistant's Expose list. Talking needs Kindo over https (or opened on the device itself) for the microphone.