Skip to content

Br0kenByDesign/Familyquest

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FamilyQuest Logo

FamilyQuest

Self-hosted Aufgaben-App für Familien mit Punkte-System und optionaler HomeAssistant-Integration

Docker PWA Node React PostgreSQL


Was ist FamilyQuest?

FamilyQuest ist eine selbst gehostete Familien-App, mit der Kinder Punkte für erledigte Aufgaben sammeln und gegen Belohnungen einlösen können. Eltern verwalten Aufgaben und Belohnungen über ein Admin-Interface. Aufgaben können manuell erstellt oder automatisiert über die REST API angelegt werden - z.B. aus HomeAssistant-Automationen heraus.

Features

Für Kinder

  • Dashboard mit aktuellem Punktestand und Fortschrittsbalken zum nächsten Ziel
  • Aufgaben einsehen und per Knopfdruck abhaken
  • Belohnungs-Shop - Punkte gegen Wünsche einlösen
  • Verlauf aller erledigten Aufgaben und eingelösten Belohnungen
  • PIN-Login - kindgerechtes Numpad statt Passwortfeld

Für Admins (Eltern)

  • Aufgaben manuell erstellen (Titel, Beschreibung, Punkte, Ablaufzeit, Zuweisung)
  • Ablaufzeiten - Aufgaben verfallen automatisch, Punkte werden auf 0 gesetzt, die Aufgabe bleibt aber sichtbar und muss trotzdem erledigt werden
  • Belohnungen verwalten (erstellen, bearbeiten, löschen)
  • Übersicht aller Kinder mit Punktestand und offenen Aufgaben
  • Verlauf aller Aktivitäten

Technisch

  • REST API - Aufgaben programmatisch erstellen mit Bearer Token Auth (eingehend - z.B. Aufgabe erstellen wenn Geschirrspüler fertig ist)
  • Webhook-Events an externe Systeme bei allen relevanten Ereignissen (ausgehend - z.B. Wenn eine neue Aufgabe zugewiesen wurde oder überfällig ist)
  • Progressive Web App - installierbar auf iOS, Android und Desktop
  • Docker Compose - ein Befehl, alles läuft
  • Setup-Wizard beim ersten Start - keine manuelle Konfiguration nötig

Screenshots

Login Dashboard Aufgaben Shop
Login Dashboard Aufgaben Shop

Quickstart

Voraussetzungen

  • Docker + Docker Compose
  • Port 3000 frei (konfigurierbar über FRONTEND_PORT)

Installation

# 1. Repository klonen
git clone https://github.com/Br0kenByDesign/familyquest.git
cd familyquest

# 2. Umgebungsvariablen konfigurieren
cp .env.example .env
nano .env
Siehe ".env Konfiguration"

# 3. Starten
docker compose up -d

# 4. App öffnen und Setup-Wizard durchlaufen
open http://localhost:3000

.env Konfiguration

# Datenbank
POSTGRES_DB=familyquest
POSTGRES_USER=familyquest
POSTGRES_PASSWORD=sicheres_passwort_hier        # ← Pflicht

# Backend
SESSION_SECRET=langer_zufaelliger_string        # ← Pflicht (openssl rand -base64 48)
CORS_ORIGINS=https://familyquest.deine-domain.de

# HomeAssistant Webhook (optional)
# FamilyQuest sendet Events an diese URL
HA_WEBHOOK_URL=https://deine-ha-instanz/api/webhook/dein-id

# Port
FRONTEND_PORT=3000

Ersteinrichtung - Setup-Wizard

Beim ersten Start erkennt FamilyQuest automatisch, dass noch keine Nutzer angelegt sind, und leitet auf den Setup-Wizard weiter.

Schritt 1 - Willkommen

image

Der Wizard begrüßt dich und erklärt den Ablauf.


Schritt 2 - Administrator

image

Lege den Admin-Account an: Name und Passwort. Dieses Passwort wird beim Login über ein Passwortfeld abgefragt.

Schritt 3 - Kinder

image

Lege die Kinder-Accounts an. Pro Kind:

  • Name - wird im Login und überall in der App angezeigt
  • PIN - 4-stellige Zahl, wird beim Login über ein Numpad eingegeben
  • Avatar - wähle aus 8 Pixel-Art Avataren (Creeper, Dino, Roboter, Katze, Stern, Rakete, Drache, Fuchs)

Bis zu 6 Kinder können angelegt werden.

Schritt 4 - Abschluss

image

Der API Key wird einmalig angezeigt. Unbedingt kopieren und sicher speichern - er wird nicht nochmal im Klartext angezeigt.

PINs und Passwörter ändern: Unter Einstellungen in der Navigation, können Kinder ihre eigenen oder Admins alle PINs und das Admin-Passwort jederzeit ändern.


Benutzung

Aufgaben erstellen (Admin)

image

Unter Aufgabe erstellen im Menü lassen sich Aufgaben manuell anlegen:

Feld Beschreibung
Titel Kurzbezeichnung der Aufgabe
Beschreibung Optionale Details
Punkte Punkte bei Erledigung
Läuft ab in Nach dieser Zeit werden Punkte auf 0 gesetzt
Für wen Zuweisung an ein Kind

Bestehende Aufgaben können in der Aufgabenliste direkt bearbeitet oder gelöscht werden.

Aufgaben abhaken (Kind)

image

Auf der Aufgaben-Seite sieht das Kind alle zugewiesenen Aufgaben. Ein Klick auf Erledigt öffnet eine Bestätigungsfrage - danach werden die Punkte sofort gutgeschrieben.

Abgelaufene Aufgaben (0 Punkte) bleiben sichtbar und können trotzdem abgehakt werden.

Belohnungen verwalten (Admin)

image

Unter Belohnungen im Menü:

  • Neue Belohnung anlegen (Emoji, Titel, Beschreibung, Punktepreis)
  • Bestehende Belohnungen bearbeiten oder löschen
  • Eigene Emojis können direkt eingegeben werden (Win+. auf Windows, Cmd+Ctrl+Space auf Mac)

Beim ersten Start werden 18 Beispiel-Belohnungen angelegt, die frei angepasst werden können.

Belohnung einlösen (Kind)

image

Im Shop sieht das Kind alle verfügbaren Belohnungen. Leistbare Belohnungen sind oben hervorgehoben. Nach Bestätigung werden die Punkte sofort abgezogen.


REST API

Aufgaben können auch über die REST API programmatisch erstellt werden - von beliebigen Systemen, Skripten oder Automationsplattformen.

Authentifizierung

Der API Key wird beim Erstellen einmalig angezeigt und dann nur noch gehasht und gesalzen gespeichert.

Authorization: Bearer fq_live_<dein-key>

Aufgabe erstellen

POST /api/v1/tasks
Authorization: Bearer fq_live_<dein-key>
Content-Type: application/json

{
  "title": "Zimmer aufräumen",
  "description": "Spielzeug wegräumen, Bett machen, Boden freiräumen",
  "points": 20,
  "assigned_to": "Kind1",
  "expires_in_minutes": 600
}

assigned_to akzeptiert den Namen des Kindes (wie beim Setup angegeben, case-insensitive) oder die UUID aus der Datenbank.

Alle Endpunkte

# Auth
POST   /auth/login
GET    /auth/me
POST   /auth/logout
GET    /auth/setup/status
POST   /auth/setup

# Aufgaben (Session oder API Key)
GET    /api/v1/tasks
POST   /api/v1/tasks
PATCH  /api/v1/tasks/:id
DELETE /api/v1/tasks/:id
POST   /api/v1/tasks/:id/complete

# Belohnungen (Session)
GET    /api/v1/rewards
POST   /api/v1/rewards
PATCH  /api/v1/rewards/:id
DELETE /api/v1/rewards/:id
POST   /api/v1/rewards/:id/redeem

# Nutzer (Session)
GET    /api/v1/users
GET    /api/v1/users/:id/history

# API Keys (Session, Admin)
GET    /api/v1/apikeys
POST   /api/v1/apikeys
DELETE /api/v1/apikeys/:id

# System
GET    /health

Home Assistant Integration

FamilyQuest lässt sich in beide Richtungen mit HomeAssistant verbinden.

Aufgaben aus HA erstellen (Home Assistant → FamilyQuest)

Option A: pyscript (empfohlen)

pyscript ist eine HACS-Integration die Python-Skripte in HomeAssistant ermöglicht. Nach der Installation über HACS muss einmalig ein Hilfsskript angelegt werden das generische HTTP-Requests kapselt.

1. pyscript über HACS installieren

HACS → Integrationen → Suche nach "pyscript" → Installieren → HA neu starten.

2. pyscript/http.py anlegen

Im HA-Konfigurationsverzeichnis (dort wo configuration.yaml liegt) den Ordner pyscript anlegen und darin eine Datei http.py mit folgendem Inhalt:

import aiohttp
import json
 
@service
async def http(
    method="GET",
    url=None,
    headers=None,
    payload=None,
    timeout=20
):
    if url is None:
        log.error("No URL provided")
        return
    try:
        final_headers = {"Content-Type": "application/json"}
        if headers:
            final_headers.update(headers)
        log.info(f"➡️ {method.upper()} {url}")
        if payload:
            log.info(f"Payload: {json.dumps(payload, indent=2)}")
        timeout_obj = aiohttp.ClientTimeout(total=timeout)
        async with aiohttp.ClientSession(timeout=timeout_obj) as session:
            async with session.request(
                method=method.upper(),
                url=url,
                headers=final_headers,
                json=payload
            ) as response:
                status = response.status
                text = await response.text()
                log.info(f"⬅️ Status: {status}")
                log.info(f"Response: {text}")
    except Exception as e:
        log.error(f"HTTP ERROR: {e}")

Danach HA neu starten. Der Service pyscript.http steht nun in allen Automationen zur Verfügung.

3. In Automationen verwenden

action: pyscript.http
data:
  method: POST
  url: https://familyquest.deine-domain.de/api/v1/tasks
  headers:
    Authorization: Bearer fq_live_<dein-key>
  payload:
    title: Geschirrspüler ausräumen
    description: Alles aus dem Spüler raus und einräumen
    points: 10
    assigned_to: Kind1
    expires_in_minutes: 1440

Der Service ist generisch und kann für beliebige HTTP-Requests genutzt werden, nicht nur für FamilyQuest.

Option B: rest_command

Ohne pyscript kann rest_command in der configuration.yaml definiert werden:

rest_command:
  familyquest_task:
    url: "https://familyquest.deine-domain.de/api/v1/tasks"
    method: POST
    headers:
      Authorization: "Bearer fq_live_<dein-key>"
      Content-Type: "application/json"
    payload: >
      {
        "title": "{{ title }}",
        "description": "{{ description }}",
        "points": {{ points | int }},
        "assigned_to": "{{ kind_name }}",
        "expires_in_minutes": {{ expires | int }}
      }

Aufruf in einer Automation:

action: rest_command.familyquest_task
data:
  title: Zimmer aufräumen
  description: Spielzeug wegräumen, Bett machen
  points: 20
  kind_name: Kind1
  expires: 600

Hinweis: rest_command unterstützt keine dynamischen Headers — der API Key ist fest in der configuration.yaml hinterlegt. pyscript ist hier flexibler.

Events empfangen (FamilyQuest → Home Assistant)

Wenn HA_WEBHOOK_URL im .env file konfiguriert ist, sendet FamilyQuest bei jedem relevanten Ereignis einen HTTP POST.

Alle Event-Typen:

// Aufgabe erledigt
{
  "event_type": "task_completed",
  "child": { "id": "...", "name": "Kind1" },
  "task":  { "id": "...", "title": "Geschirrspüler ausräumen", "points": 10 }
}

// Belohnung eingelöst
{
  "event_type": "reward_redeemed",
  "child":  { "id": "...", "name": "Kind1" },
  "reward": { "id": "...", "title": "Eis essen gehen", "points_cost": 200 }
}

// Aufgabe abgelaufen (0 Punkte, aber noch sichtbar)
{
  "event_type": "task_expired",
  "child": { "id": "...", "name": "Kind1" },
  "task":  { "id": "...", "title": "Zimmer aufräumen" }
}

// Offene Aufgaben pro Kind - alle 15 Minuten
{
  "event_type": "open_tasks_summary",
  "child":           { "id": "...", "name": "Kind1" },
  "open_task_count": 3
}

Beispiel-Automation - Webhook empfangen und verarbeiten:

automation:
  - alias: "FamilyQuest Webhook"
    trigger:
      - platform: webhook
        webhook_id: "dein-webhook-id"
        allowed_methods: [POST]
    actions:
      - choose:
          - conditions:
              - condition: template
                value_template: "{{ trigger.json.event_type == 'task_completed' }}"
            sequence:
              - action: notify.mobile_app_dein_handy
                data:
                  title: "Aufgabe erledigt ✅"
                  message: >
                    {{ trigger.json.child.name }} hat
                    "{{ trigger.json.task.title }}" abgeschlossen
                    (+{{ trigger.json.task.points }} Punkte)

          - conditions:
              - condition: template
                value_template: "{{ trigger.json.event_type == 'reward_redeemed' }}"
            sequence:
              - action: notify.mobile_app_dein_handy
                data:
                  title: "Belohnung eingelöst 🎁"
                  message: >
                    {{ trigger.json.child.name }} hat
                    "{{ trigger.json.reward.title }}" eingelöst
                    ({{ trigger.json.reward.points_cost }} Punkte)

          - conditions:
              - condition: template
                value_template: "{{ trigger.json.event_type == 'task_expired' }}"
            sequence:
              - action: notify.mobile_app_dein_handy
                data:
                  title: "Aufgabe abgelaufen ❌"
                  message: >
                    {{ trigger.json.child.name }} hat
                    "{{ trigger.json.task.title }}" nicht erledigt

          - conditions:
              - condition: template
                value_template: "{{ trigger.json.event_type == 'open_tasks_summary' }}"
            sequence:
              # Beispiel: Anzahl offener Aufgaben in einem Helper speichern
              # und darauf basierend Push-Nachrichten oder Automationen auslösen
              - action: input_number.set_value
                target:
                  entity_id: input_number.offene_aufgaben_kind1
                data:
                  value: "{{ trigger.json.open_task_count }}"

Dashboard: Der open_tasks_summary response Type der outgoing webhooks überträgt alle offenen Aufgaben der Kinder. Diese Werte können als helper eingerichtet werden, sodass offene Aufgaben auch auf dem Dashboard sichtbar gemacht werden können.

image

Als PWA installieren

FamilyQuest ist eine Progressive Web App und kann auf allen Geräten wie eine native App installiert werden - kein App Store nötig.

  • iOS: Safari öffnen → Teilen-Symbol → Zum Home-Bildschirm
  • Android: Chrome öffnen → Menü (⋮) → App installieren
  • Desktop (Chrome/Edge): In der Adressleiste erscheint ein Install-Symbol rechts

Nach der Installation startet die App ohne Browser-UI, hat ein eigenes Icon auf dem Homescreen und verhält sich wie eine native App - inklusive Offline-Unterstützung für bereits geladene Inhalte.

image

Architektur

┌─────────────────────────────────────────────┐
│                   Host                      │
│                                             │
│  ┌──────────────┐      ┌─────────────────┐  │
│  │   Frontend   │      │    Backend      │  │
│  │  React + PWA │────▶│  Node.js/Express│  │
│  │  Nginx :80   │proxy │  :3001 (intern) │  │
│  └──────────────┘      └────────┬────────┘  │
│         ▲                       │           │
│         │                ┌──────▼───────┐   │
│    Browser /             │  PostgreSQL  │   │
│    PWA Install           │  :5432       │   │
│                          └──────────────┘   │
└─────────────────────────────────────────────┘
         ▲                        │
         │                        ▼
  Externes System ◀──────── Webhooks (optional)
         │
         └──── REST API ──────▶ POST /api/v1/tasks

Drei Docker-Container: Frontend (nginx, React-Build), Backend (Node.js/Express), Postgres. Das Backend ist nicht extern erreichbar - nginx proxied alle /api/-Anfragen intern.


Sicherheit

  • API Keys - bcrypt mit 12 Rounds + Salt, Plaintext wird nie gespeichert, Key nur einmalig bei Erstellung sichtbar
  • PINs & Passwörter - bcrypt mit 12 Rounds + Salt
  • Sessions - httpOnly, SameSite=lax, Secure hinter HTTPS, SQLite-Store
  • Rate Limiting - 500 req/15min (API), 60 req/15min (Login)
  • Helmet.js - Security Headers auf allen Responses
  • CORS - strikt auf konfigurierte Origins beschränkt
  • Input-Validierung - Zod auf allen schreibenden Endpunkten
  • Rollen - Admin/Child-Trennung auf jedem Backend-Endpunkt durchgesetzt
  • Kein direkter DB-Zugriff - Backend nicht extern exposed

Update

git pull
docker compose up -d --build

Datenbank-Migrationen laufen automatisch beim Backend-Start.


Backup & Restore

# Backup
docker exec familyquest-db pg_dump -U familyquest familyquest > backup_$(date +%Y%m%d).sql

# Restore
cat backup_20260522.sql | docker exec -i familyquest-db psql -U familyquest familyquest

Tech Stack

Schicht Technologie
Frontend React 19, Vite, TailwindCSS, React Router
PWA vite-plugin-pwa, Workbox
Backend Node.js 20, Express, Prisma ORM
Datenbank PostgreSQL 16
Auth express-session, bcrypt, PIN/Passwort-Login
Scheduling node-cron
Container Docker, Nginx

Lizenz

MIT

About

Self-hosted family chore app - kids earn points for tasks, redeem them for rewards. HomeAssistant integration via REST API and webhooks. Docker Compose, PWA, PostgreSQL. Currently only in german

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages