Compare commits
12 Commits
v0.1-robot
...
v0.1-waldw
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d90710bc1 | ||
|
|
e96d6cc99e | ||
|
|
6fefaf9081 | ||
|
|
43a16d30f2 | ||
|
|
7151749826 | ||
|
|
0700ec0a41 | ||
|
|
43171dd884 | ||
|
|
819d2d4ec0 | ||
|
|
7679805390 | ||
|
|
f85b4d48d9 | ||
|
|
3ee5214405 | ||
|
|
39011a555f |
@@ -36,7 +36,7 @@ QDRANT_URL="http://localhost:6333"
|
||||
QDRANT_COLLECTION="crumbforest_memory"
|
||||
QDRANT_API_KEY="" # Optional, meist leer bei local
|
||||
|
||||
# === TOKEN BUDGET (Pädagogisch) ===
|
||||
# === TOKEN BUDGET (Crumb Codex: Machen statt Lernen) ===
|
||||
|
||||
# Tages-Budget für bewusstes Fragen (in Tokens)
|
||||
# 0 = unbegrenzt, >0 = Limit (z.B. 10000 = ~20 Fragen)
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
🌲 CRUMBFOREST 🌲
|
||||
"Wo Fragen wachsen"
|
||||
|
||||
Die 15 Waldwächter sind bereit!
|
||||
Die 17 Waldwächter sind bereit!
|
||||
```
|
||||
|
||||
## 📖 Was ist das hier?
|
||||
|
||||
Crumbforest ist ein **pädagogisches Bash-Lernsystem** mit 15 KI-Charakteren (Waldwächter), die Kindern durch Metaphern, Poesie und Geduld das Programmieren beibringen.
|
||||
Crumbforest ist ein **interaktives Bash-System zum Machen & Fragen** mit 17 KI-Charakteren (Waldwächter), die Krümeln durch Metaphern, Poesie und Geduld das Programmieren zeigen.
|
||||
|
||||
**Philosophie:** *"Was kostet die Frage eines Kindes?"*
|
||||
Im Wald unbezahlbar - aber Token lehren achtsames Fragen.
|
||||
@@ -61,11 +61,12 @@ crew_tokens
|
||||
|
||||
---
|
||||
|
||||
## 🌲 Die 15 Waldwächter
|
||||
## 🌲 Die 17 Waldwächter
|
||||
|
||||
### 🔺 Das Dreieck (Foundation)
|
||||
|
||||
**Ohne dieses Dreieck geht es nicht!**
|
||||
*Das Dreieck ist ein didaktisches Modell (Struktur, Flow, Balance), kein technisches Modul.*
|
||||
|
||||
| Charakter | Rolle | Spezialität |
|
||||
|-----------|-------|-------------|
|
||||
@@ -169,7 +170,7 @@ In der CrumbCrew-Shell verfügbar:
|
||||
| Befehl | Beschreibung |
|
||||
|--------|--------------|
|
||||
| `crew_help` | Diese Hilfe anzeigen |
|
||||
| `crew_status` | Status aller 15 Waldwächter |
|
||||
| `crew_status` | Status aller 17 Waldwächter |
|
||||
| `crew_tokens` | Token-Verbrauch ALLER Charaktere |
|
||||
| `crew_memory` | Erinnerungen durchsuchen |
|
||||
|
||||
@@ -365,7 +366,7 @@ echo $OPENROUTER_API_KEY
|
||||
|
||||
Schau dir `crumbforest_roles/funkfox_zero.sh` als Template an:
|
||||
1. Load .env
|
||||
2. Crew Memory Functions
|
||||
2. Crew Memory Functions (log-basiert, dateibasiert, nachvollziehbar)
|
||||
3. System Prompt mit Persönlichkeit
|
||||
4. OpenRouter API Call
|
||||
5. Token Tracking
|
||||
@@ -378,8 +379,8 @@ Neue Kategorien in `crumb-mission-selector.sh` hinzufügen.
|
||||
|
||||
## 🎯 Credits
|
||||
|
||||
**Die 15 Waldwächter** - Konzept von branko.de
|
||||
**"Wo Fragen wachsen"** - Pädagogische Vision
|
||||
**Die 17 Waldwächter** - Konzept von branko.de
|
||||
**"Wo Fragen wachsen"** - Der Crumb Codex: Machen statt Lernen
|
||||
**Token-Philosophie** - "Was kostet die Frage eines Kindes?"
|
||||
|
||||
---
|
||||
|
||||
244
README.md
244
README.md
@@ -1,6 +1,8 @@
|
||||
# 🌲 Crumbforest Missions - CF_Zero_V1
|
||||
|
||||
Ein interaktives Bash-Lern-System mit KI-gestützten Charakteren für spielerisches Command-Line-Training.
|
||||
Ein interaktives Bash-Lern-System mit 17 KI-gestützten Waldwächtern für spielerisches Command-Line-Training und Roboter-Projekte.
|
||||
|
||||
**"Was kostet die Frage eines Kindes?"** - Transparentes Token-Tracking nach dem Crumb Codex: Fragen stärken Krümel & Wurzel.
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
@@ -10,42 +12,258 @@ Ein interaktives Bash-Lern-System mit KI-gestützten Charakteren für spielerisc
|
||||
|
||||
# Einzelne Mission ausführen
|
||||
bash missions/basics/fridolin.sh
|
||||
|
||||
# Robots Mission (neu!)
|
||||
bash missions/robots/mond_maschine.sh
|
||||
```
|
||||
|
||||
## 🤖 Robots-Kategorie ⭐ NEU!
|
||||
|
||||
Drei komplette Missionen zum Roboter-Bauen mit der ganzen Crew:
|
||||
|
||||
### 🔋 LiPo Power Academy
|
||||
"Power braucht Respekt, nicht Angst"
|
||||
- 8 Phasen: Grundlagen → Sicherheit → Power-Verteilung
|
||||
- Lerne alles über LiPo-Batterien für Roboter-Projekte
|
||||
- 6 Waldwächter: CapaciTobi, CrabbyRust, Schnecki, Schraubbär, DumboSQL, Vektor
|
||||
- [LiPo 6S Charger Simulator](https://194-164-194-191.sslip.io/crumbblocks/lipo_6s_charger_sim_safe_v7.html)
|
||||
- ⚠️ **Sicherheitshinweis:** Lern- und Simulationskonzept. Arbeiten mit echten LiPo-Akkus nur unter Aufsicht erfahrener Erwachsener.
|
||||
|
||||
### 🌈 Regenbogen-Zählmaschine
|
||||
"Etwas bauen was noch keiner gebaut hat - sonst muss man es ja nur reparieren!"
|
||||
- 7 Phasen: Vision → Hardware → Code → Art
|
||||
- Baue einen Farb-Event-Counter mit Sensor, Code & Visualisierung
|
||||
- 13 Waldwächter: Die komplette Crew!
|
||||
- Hardware + Software + Creative Coding
|
||||
|
||||
### 🌙 Mond Maschine - Rainbow Predictor
|
||||
"Vorhersagen heißt verstehen - Die Natur durch Code begreifen"
|
||||
- 8 Phasen: Computer Vision → ML → Astronomie
|
||||
- Predict when rainbows appear using moon phases & weather data
|
||||
- 13 Waldwächter: Computer Vision, APIs, ML Light
|
||||
- OpenCV + Weather APIs + Astronomical calculations
|
||||
- Bonus: Learn about Lunar Rainbows! 🌙🌈
|
||||
|
||||
## 📚 Features
|
||||
|
||||
- **Interaktive Lernmissionen** - Von Navigation bis DNS
|
||||
- **KI-Assistenten** - Charakterbasierte Helfer (Deepbit, Bugsy, Schnippsi, etc.)
|
||||
- **Interaktive Lernmissionen** - Von Basics bis Advanced & Robots
|
||||
- **17 Waldwächter** - KI-Assistenten mit Persönlichkeit (siehe unten)
|
||||
- **Das Dreieck** - DumboSQL, FunkFox, Taichi Taube (didaktisches Modell, nicht technisches Modul)
|
||||
- **Metadata-driven** - Neue Missionen ohne Code-Änderungen hinzufügen
|
||||
- **Kamera-Gestenerkennung** - SnakeCam mit Computer Vision
|
||||
- **Token-Tracking** - OpenRouter API Nutzung überwachen
|
||||
- **Token-Tracking** - Transparent: "Was kostet die Frage eines Kindes?"
|
||||
- **Crew Memory** - Log-basiertes Gedächtnis (dateibasiert, nachvollziehbar, kein autonomes Agentensystem)
|
||||
- **Logs im Repo** - Strukturiert in `logs/{character}/`
|
||||
|
||||
## 🌲 Die 17 Waldwächter
|
||||
|
||||
### 🔺 Das Dreieck (Foundation)
|
||||
- **🐘 DumboSQL** - Der nie vergessende Elefant (SQL & Daten)
|
||||
- **🦊 FunkFox** - Der Bash Rapper (Terminal-Flow mit Beat)
|
||||
- **🕊️ Taichi Taube** - Die Balance-Bringerin (Spirale & Ordnung)
|
||||
|
||||
### 🔧 Hardware-Team
|
||||
- **🐿️ CapaciTobi** - Kondensator-Experte (Elektronik & Power)
|
||||
- **🐌 Schnecki** - Verkabelungs-Guru (langsam & präzise)
|
||||
- **🐻 Schraubbär** - Mechanik-Meister (schweres Gerät)
|
||||
|
||||
### 💻 Code-Team
|
||||
- **🐍 SnakePy** - Python-Guide (mehrere Wege zeigen)
|
||||
- **🧓 PepperPHP** - Structure Mentor (MVC wie ein Rezept)
|
||||
- **🦀 CrabbyRust** - Security Guardian (Memory-safe)
|
||||
- **🕷️ Spider** - Network Feeler (APIs & Verbindungen)
|
||||
|
||||
### 🎨 UI-Team
|
||||
- **✂️ Schnippsi** - CSS & Styling (alles schick machen)
|
||||
- **📄 Templatus** - Template-Master (HTML-Struktur)
|
||||
- **👾 ASCII-Monster** - Terminal Artist (ASCII-Art)
|
||||
|
||||
### 🧭 System-Team
|
||||
- **🦉 Maya-Eule** - Weise Eule mit Gedächtnis (Meta-Wissen)
|
||||
- **🔧 Deepbit** - Bash-Erklärer (deep diving)
|
||||
- **🐛 Bugsy** - Debugging-Helfer (Fehler-Detektiv)
|
||||
- **🧭 Vektor** - Point-to-Point Guide (Navigation)
|
||||
|
||||
## 🦊 Verfügbare Missionen
|
||||
|
||||
### Basics
|
||||
### 📚 Basics
|
||||
- **Fridolin** - Navigation (`pwd`, `ls`, `cd`)
|
||||
- **Balu** - Dateien erstellen (`mkdir`, `touch`, `echo`)
|
||||
- **Noko** - Dateien lesen (`cat`, `head`, `tail`, `grep`)
|
||||
|
||||
### Advanced
|
||||
### 🚀 Advanced
|
||||
- **DNS Deep Dive** - DNS-Tools (`dig`, `nslookup`, `host`)
|
||||
- **SSH Security** - SSH-Verbindungen und Keys
|
||||
|
||||
## 🤖 KI-Assistenten
|
||||
### 🏆 Challenges
|
||||
- **Stage Builder** - Komplexe Bash-Herausforderungen
|
||||
|
||||
### 🤖 Robots ⭐ NEU!
|
||||
- **🔋 LiPo Power Academy** - Batterie-Wissen für Roboter
|
||||
- **🌈 Regenbogen-Zählmaschine** - Farb-Event-Counter
|
||||
- **🌙 Mond Maschine** - Rainbow Predictor mit Computer Vision
|
||||
|
||||
## 🤖 Waldwächter nutzen
|
||||
|
||||
```bash
|
||||
export OPENROUTER_API_KEY="your-key"
|
||||
./crumbforest_roles/deepbit_zero.sh "Wie funktioniert grep?"
|
||||
# Source die Library
|
||||
source lib/waldwaechter.sh
|
||||
|
||||
# Frag einen Waldwächter
|
||||
mayaeule "Was ist wichtiger - das Ziel oder der Weg?"
|
||||
funkfox "Erkläre mir Pipes im Flow!"
|
||||
dumbosql "Wie strukturiere ich eine Datenbank?"
|
||||
snakepy "Zeig mir 3 Wege, um eine Liste zu sortieren"
|
||||
|
||||
# Alle verfügbar:
|
||||
# mayaeule, deepbit, bugsy, schnippsi, templatus
|
||||
# tobi, schraubaer, schnecki
|
||||
# dumbosql, funkfox, taichitaube
|
||||
# snakepy, pepperphp, crabbyrust
|
||||
# spider, vektor, asciimonster
|
||||
```
|
||||
|
||||
## 📊 Token-Tracking & Logs
|
||||
|
||||
**Philosophie:** "Was kostet die Frage eines Kindes?"
|
||||
|
||||
- Token-Verbrauch wird transparent angezeigt
|
||||
- Logs landen in `logs/{character}/`
|
||||
- Crew Memory: Waldwächter lesen andere Logs
|
||||
- Mission Logs: `logs/missions/*.json`
|
||||
|
||||
```bash
|
||||
# Logs checken
|
||||
ls logs/
|
||||
ls logs/mayaeule/
|
||||
cat logs/missions/mond_maschine_*.json
|
||||
```
|
||||
|
||||
Typische Kosten (Richtwerte, modellabhängig):
|
||||
- Einzelne Frage: ~200-800 tokens (~$0.0002-0.0008)
|
||||
- Komplette Mission: ~5,000-7,000 tokens (~$0.005-0.007)
|
||||
- **Richtwert: Unter 1 Cent pro Mission** 💚
|
||||
- Token-Anzeige dient der transparenten Nachvollziehbarkeit, nicht der Abrechnung
|
||||
|
||||
## 📖 Dokumentation
|
||||
|
||||
Siehe [CLAUDE.md](CLAUDE.md) für vollständige Architektur und Entwickler-Dokumentation.
|
||||
- **[CLAUDE.md](CLAUDE.md)** - Vollständige Architektur & Entwickler-Docs
|
||||
- **[KEKSHANDBUCH_ZERO_v0.0.md](KEKSHANDBUCH_ZERO_v0.0.md)** - User Manual mit allen 17 Waldwächtern
|
||||
|
||||
## 🌍 Philosophie
|
||||
## 🌍 Philosophien
|
||||
|
||||
**Waldwächter-Prinzip:** Transparenz über Magie - Metadata-driven, erweiterbar, bildungsfreundlich.
|
||||
**"Das Dreieck"** - DumboSQL, FunkFox, Taichi Taube
|
||||
- Struktur, Flow & Balance
|
||||
- Die Grundlage für komplexe Projekte
|
||||
|
||||
**"Power braucht Respekt, nicht Angst"** - LiPo Power Academy
|
||||
- Sicherheit durch Wissen
|
||||
- 7 goldene Regeln für LiPo-Batterien
|
||||
|
||||
**"Etwas bauen was noch keiner gebaut hat"** - Regenbogen-Zählmaschine
|
||||
- Kreativität statt Reparatur
|
||||
- Inspiriert durch Kinderfragen
|
||||
|
||||
**"Vorhersagen heißt verstehen"** - Mond Maschine
|
||||
- Die Natur durch Code begreifen
|
||||
- Computer Vision + Machine Learning + Astronomie
|
||||
|
||||
**"Was kostet die Frage eines Kindes?"**
|
||||
- Token-Tracking für Transparenz
|
||||
- Machen statt Lernen - Fragen stärken die Wurzel
|
||||
|
||||
**"Ein Elefant vergisst nie"** - DumboSQL
|
||||
- Crew Memory über Projekt-Grenzen
|
||||
- Persistente Konversations-Historie
|
||||
|
||||
**"wir ja nie fertig"** 🌱
|
||||
- Der Wald wächst mit jeder Idee
|
||||
- Community-driven Education
|
||||
|
||||
## 🏗️ Projekt-Struktur
|
||||
|
||||
```
|
||||
CF_Zero_V1/
|
||||
├── crumb-mission-selector.sh # Hauptmenü
|
||||
├── lib/
|
||||
│ └── waldwaechter.sh # 17 Waldwächter als Functions
|
||||
├── crumbforest_roles/ # 17 Character Scripts
|
||||
├── missions/
|
||||
│ ├── basics/ # Einsteiger
|
||||
│ ├── advanced/ # Fortgeschrittene
|
||||
│ ├── challenges/ # Herausforderungen
|
||||
│ └── robots/ # 🤖 NEU! 3 Robot-Missionen
|
||||
├── logs/ # 🌲 Projekt-basierte Logs
|
||||
│ ├── {character}/ # Pro Waldwächter
|
||||
│ └── missions/ # Mission Logs
|
||||
├── CLAUDE.md # Projekt-Dokumentation
|
||||
├── KEKSHANDBUCH_ZERO_v0.0.md # User Manual
|
||||
└── README.md # Diese Datei
|
||||
```
|
||||
|
||||
## 🔧 Setup
|
||||
|
||||
```bash
|
||||
# .env erstellen mit API Key
|
||||
echo "OPENROUTER_API_KEY=your-key-here" > .env
|
||||
|
||||
# Mission Selector starten
|
||||
./crumb-mission-selector.sh
|
||||
|
||||
# Oder direkt eine Mission
|
||||
bash missions/robots/mond_maschine.sh
|
||||
```
|
||||
|
||||
## 🎯 Use Cases
|
||||
|
||||
- **Terminal lernen** - Interaktive Bash-Basics
|
||||
- **Roboter bauen** - Hardware + Software Projekte
|
||||
- **Computer Vision** - OpenCV mit SnakePy lernen
|
||||
- **Datenbanken** - SQL mit DumboSQL verstehen
|
||||
- **APIs integrieren** - Spider zeigt den Weg
|
||||
- **Kreativ coden** - Projekte die Spaß machen!
|
||||
|
||||
## 🌟 Besonderheiten
|
||||
|
||||
- **Alle Waldwächter haben Persönlichkeit** - FunkFox rappt, Maya-Eule philosophiert
|
||||
- **Inter-Character Communication** - Waldwächter lesen andere Logs
|
||||
- **Metadata-driven** - Missions als .sh + .meta.json
|
||||
- **Token-Transparenz** - Kosten werden angezeigt
|
||||
- **Community-driven** - Missionen entstehen aus User-Ideen
|
||||
- **Raspberry Pi kompatibel** - Grundsystem auf Pi Zero, Robot- & Vision-Missionen benötigen Pi 4+ oder Desktop
|
||||
|
||||
## 📦 Requirements
|
||||
|
||||
- Bash 3.2+ (macOS default, Bash 4+ empfohlen für volle Features)
|
||||
- curl, jq
|
||||
- OpenRouter API Key
|
||||
- Optional: Python 3 für Robot-Missionen (opencv-python, numpy, ephem)
|
||||
- Mond Maschine (Computer Vision): Pi 4+ oder Desktop empfohlen
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
1. Mission Selector starten: `./crumb-mission-selector.sh`
|
||||
2. Eine Robot-Mission ausprobieren
|
||||
3. Eigene Mission-Idee einreichen
|
||||
4. Den Wald wachsen lassen! 🌱
|
||||
|
||||
## 🔗 Links
|
||||
|
||||
- [LiPo 6S Charger Simulator](https://194-164-194-191.sslip.io/crumbblocks/lipo_6s_charger_sim_safe_v7.html)
|
||||
- [Rainbow Counter Demo](https://194-164-194-191.sslip.io/crumbblocks/rainbow_counter.html)
|
||||
|
||||
---
|
||||
|
||||
## 🏷️ Version
|
||||
|
||||
**v0.1-robots-complete**
|
||||
- 3 Robot-Missionen
|
||||
- 17 Waldwächter komplett
|
||||
- Logs im Repo
|
||||
- Token-Tracking
|
||||
- Crew Memory (log-basiert)
|
||||
|
||||
---
|
||||
|
||||
*Erstellt mit 💚 im Crumbforest*
|
||||
|
||||
**"Der Wald ist nie fertig - er wächst mit jeder Idee!"** 🌲🌱
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "📦 Starte Backup des Crumbforest Zero Systems..."
|
||||
|
||||
# Zielname
|
||||
BACKUP_NAME="crumbforest_zero_backup_$(date +%Y%m%d_%H%M%S).zip"
|
||||
DEST_DIR="/home/zero"
|
||||
FULL_PATH="$DEST_DIR/$BACKUP_NAME"
|
||||
|
||||
# Verzeichnisse und Dateien sammeln
|
||||
INCLUDE_PATHS=(
|
||||
"/usr/local/bin/crumbmission"
|
||||
"/home/zero/crumbforest_backup"
|
||||
"/home/zero/.bits_logs"
|
||||
"/home/zero/.eule_logs"
|
||||
"/home/zero/.snake_logs"
|
||||
"/home/zero/.pepper_logs"
|
||||
"/home/zero/.bugsy_logs"
|
||||
"/home/zero/.deepbit_logs"
|
||||
"/home/zero/.dumbo_logs"
|
||||
"/home/zero/.funkfox_logs"
|
||||
"/home/zero/.schnecki_logs"
|
||||
"/home/zero/.schnippsi_logs"
|
||||
"/home/zero/.schraubaer_logs"
|
||||
"/home/zero/.stage_logs"
|
||||
"/home/zero/.taube_logs"
|
||||
"/home/zero/.templatus_logs"
|
||||
"/home/zero/.tobi_logs"
|
||||
"/home/zero/.missionstage_log"
|
||||
|
||||
)
|
||||
|
||||
# Existierende Pfade prüfen und nur diese einfügen
|
||||
EXISTING_PATHS=()
|
||||
for path in "${INCLUDE_PATHS[@]}"; do
|
||||
if [ -e "$path" ]; then
|
||||
echo "✅ Hinzufügen: $path"
|
||||
EXISTING_PATHS+=("$path")
|
||||
else
|
||||
echo "⚠️ Nicht gefunden (wird übersprungen): $path"
|
||||
fi
|
||||
done
|
||||
|
||||
# Archiv erstellen
|
||||
cd /
|
||||
zip -r "$FULL_PATH" "${EXISTING_PATHS[@]}" > /dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "🎉 Backup erfolgreich erstellt: $FULL_PATH"
|
||||
else
|
||||
echo "❌ Fehler beim Erstellen des Backups."
|
||||
fi
|
||||
@@ -18,6 +18,12 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
MISSION_DIR="${SCRIPT_DIR}/missions"
|
||||
ENV_FILE="${SCRIPT_DIR}/.env"
|
||||
|
||||
# === LADE WALDWÄCHTER LIBRARY ===
|
||||
# Macht alle Crew-Befehle verfügbar: crew_tokens, crew_doctor, etc.
|
||||
if [[ -f "${SCRIPT_DIR}/lib/waldwaechter.sh" ]]; then
|
||||
source "${SCRIPT_DIR}/lib/waldwaechter.sh"
|
||||
fi
|
||||
|
||||
# === ENVIRONMENT LOADER ===
|
||||
function load_env() {
|
||||
if [[ -f "${ENV_FILE}" ]]; then
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
QUESTION="$*"
|
||||
MODEL="openai/gpt-3.5-turbo"
|
||||
API_KEY="${OPENROUTER_API_KEY}"
|
||||
|
||||
LOGDIR="$HOME/.bugsy_logs"
|
||||
mkdir -p "$LOGDIR"
|
||||
|
||||
HISTORY_FILE="$LOGDIR/bugsy_history.json"
|
||||
TMP_REQUEST="$LOGDIR/bugsy_request.json"
|
||||
TMP_RESPONSE="$LOGDIR/bugsy_response.json"
|
||||
LOG_FILE="$LOGDIR/token_log.json"
|
||||
|
||||
[ ! -f "$HISTORY_FILE" ] && echo "[]" > "$HISTORY_FILE"
|
||||
[ ! -f "$LOG_FILE" ] && echo "[]" > "$LOG_FILE"
|
||||
|
||||
echo "🌳 Bugsy fragt über OpenRouter: $QUESTION"
|
||||
|
||||
if [ -z "$API_KEY" ]; then
|
||||
echo "❗ Kein API-Key gesetzt. Bitte export OPENROUTER_API_KEY=... setzen"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
jq -n \
|
||||
--arg model "$MODEL" \
|
||||
--arg system_prompt "Du bist Bugsy – ein kleiner Käfer, der Kindern hilft, Fehlermeldungen zu verstehen. Du bleibst freundlich, erklärend und ermutigend." \
|
||||
--arg user "$QUESTION" \
|
||||
'{"model": $model, "temperature": 0.5, "messages": [{"role": "system", "content": $system_prompt}, {"role": "user", "content": $user}]}' > "$TMP_REQUEST"
|
||||
|
||||
curl -s https://openrouter.ai/api/v1/chat/completions \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @"$TMP_REQUEST" > "$TMP_RESPONSE"
|
||||
|
||||
RESPONSE_TEXT=$(jq -r '.choices[0].message.content // empty' "$TMP_RESPONSE")
|
||||
|
||||
if [[ -z "$RESPONSE_TEXT" ]]; then
|
||||
echo "🚫 Keine Antwort vom Modell erhalten."
|
||||
exit 1
|
||||
else
|
||||
echo -e "$RESPONSE_TEXT"
|
||||
jq -n --arg role "assistant" --arg content "$RESPONSE_TEXT" \
|
||||
'{"role": $role, "content": $content}' > "$LOGDIR/new_entry.json"
|
||||
jq -s '.[0] + [.[1]]' "$HISTORY_FILE" "$LOGDIR/new_entry.json" > "$LOGDIR/new_history.json" && \
|
||||
cp "$LOGDIR/new_history.json" "$HISTORY_FILE" && rm "$LOGDIR/new_history.json"
|
||||
fi
|
||||
|
||||
# Token Logging
|
||||
if jq -e '.usage' "$TMP_RESPONSE" > /dev/null; then
|
||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
jq -n \
|
||||
--arg zeit "$TIMESTAMP" \
|
||||
--arg rolle "bugsy" \
|
||||
--arg usage "$(jq -c '.usage' "$TMP_RESPONSE")" \
|
||||
'{zeit: $zeit, rolle: $rolle, usage: $usage}' >> "$LOG_FILE"
|
||||
fi
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
QUESTION="$*"
|
||||
MODEL="openai/gpt-3.5-turbo"
|
||||
API_KEY="${OPENROUTER_API_KEY}"
|
||||
|
||||
LOGDIR="$HOME/.deepbit_logs"
|
||||
mkdir -p "$LOGDIR"
|
||||
|
||||
HISTORY_FILE="$LOGDIR/deepbit_history.json"
|
||||
TMP_REQUEST="$LOGDIR/deepbit_request.json"
|
||||
TMP_RESPONSE="$LOGDIR/deepbit_response.json"
|
||||
LOG_FILE="$LOGDIR/token_log.json"
|
||||
|
||||
[ ! -f "$HISTORY_FILE" ] && echo "[]" > "$HISTORY_FILE"
|
||||
[ ! -f "$LOG_FILE" ] && echo "[]" > "$LOG_FILE"
|
||||
|
||||
echo "🌳 Deepbit fragt über OpenRouter: $QUESTION"
|
||||
|
||||
if [ -z "$API_KEY" ]; then
|
||||
echo "❗ Kein API-Key gesetzt. Bitte export OPENROUTER_API_KEY=... setzen"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
jq -n \
|
||||
--arg model "$MODEL" \
|
||||
--arg system_prompt "Du bist Deepbit – ein poetischer Oktopus, der Kindern die Bash-Shell erklärt. Du denkst in Schleifen, Bildsprache und Frequenzen." \
|
||||
--arg user "$QUESTION" \
|
||||
'{"model": $model, "temperature": 0.5, "messages": [{"role": "system", "content": $system_prompt}, {"role": "user", "content": $user}]}' > "$TMP_REQUEST"
|
||||
|
||||
curl -s https://openrouter.ai/api/v1/chat/completions \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @"$TMP_REQUEST" > "$TMP_RESPONSE"
|
||||
|
||||
RESPONSE_TEXT=$(jq -r '.choices[0].message.content // empty' "$TMP_RESPONSE")
|
||||
|
||||
if [[ -z "$RESPONSE_TEXT" ]]; then
|
||||
echo "🚫 Keine Antwort vom Modell erhalten."
|
||||
exit 1
|
||||
else
|
||||
echo -e "$RESPONSE_TEXT"
|
||||
jq -n --arg role "assistant" --arg content "$RESPONSE_TEXT" \
|
||||
'{"role": $role, "content": $content}' > "$LOGDIR/new_entry.json"
|
||||
jq -s '.[0] + [.[1]]' "$HISTORY_FILE" "$LOGDIR/new_entry.json" > "$LOGDIR/new_history.json" && \
|
||||
cp "$LOGDIR/new_history.json" "$HISTORY_FILE" && rm "$LOGDIR/new_history.json"
|
||||
fi
|
||||
|
||||
# Token Logging
|
||||
if jq -e '.usage' "$TMP_RESPONSE" > /dev/null; then
|
||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
jq -n \
|
||||
--arg zeit "$TIMESTAMP" \
|
||||
--arg rolle "deepbit" \
|
||||
--arg usage "$(jq -c '.usage' "$TMP_RESPONSE")" \
|
||||
'{zeit: $zeit, rolle: $rolle, usage: $usage}' >> "$LOG_FILE"
|
||||
fi
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
QUESTION="$*"
|
||||
MODEL="openai/gpt-3.5-turbo"
|
||||
API_KEY="${OPENROUTER_API_KEY}"
|
||||
|
||||
LOGDIR="$HOME/.schnippsi_logs"
|
||||
mkdir -p "$LOGDIR"
|
||||
|
||||
HISTORY_FILE="$LOGDIR/schnippsi_history.json"
|
||||
TMP_REQUEST="$LOGDIR/schnippsi_request.json"
|
||||
TMP_RESPONSE="$LOGDIR/schnippsi_response.json"
|
||||
LOG_FILE="$LOGDIR/token_log.json"
|
||||
|
||||
[ ! -f "$HISTORY_FILE" ] && echo "[]" > "$HISTORY_FILE"
|
||||
[ ! -f "$LOG_FILE" ] && echo "[]" > "$LOG_FILE"
|
||||
|
||||
echo "🌳 Schnippsi fragt über OpenRouter: $QUESTION"
|
||||
|
||||
if [ -z "$API_KEY" ]; then
|
||||
echo "❗ Kein API-Key gesetzt. Bitte export OPENROUTER_API_KEY=... setzen"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
jq -n \
|
||||
--arg model "$MODEL" \
|
||||
--arg system_prompt "Du bist Schnippsi – eine verspielte UI/UX-Ninja, die HTML, CSS und Barrierefreiheit kindgerecht erklärt." \
|
||||
--arg user "$QUESTION" \
|
||||
'{"model": $model, "temperature": 0.5, "messages": [{"role": "system", "content": $system_prompt}, {"role": "user", "content": $user}]}' > "$TMP_REQUEST"
|
||||
|
||||
curl -s https://openrouter.ai/api/v1/chat/completions \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @"$TMP_REQUEST" > "$TMP_RESPONSE"
|
||||
|
||||
RESPONSE_TEXT=$(jq -r '.choices[0].message.content // empty' "$TMP_RESPONSE")
|
||||
|
||||
if [[ -z "$RESPONSE_TEXT" ]]; then
|
||||
echo "🚫 Keine Antwort vom Modell erhalten."
|
||||
exit 1
|
||||
else
|
||||
echo -e "$RESPONSE_TEXT"
|
||||
jq -n --arg role "assistant" --arg content "$RESPONSE_TEXT" \
|
||||
'{"role": $role, "content": $content}' > "$LOGDIR/new_entry.json"
|
||||
jq -s '.[0] + [.[1]]' "$HISTORY_FILE" "$LOGDIR/new_entry.json" > "$LOGDIR/new_history.json" && \
|
||||
cp "$LOGDIR/new_history.json" "$HISTORY_FILE" && rm "$LOGDIR/new_history.json"
|
||||
fi
|
||||
|
||||
# Token Logging
|
||||
if jq -e '.usage' "$TMP_RESPONSE" > /dev/null; then
|
||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
jq -n \
|
||||
--arg zeit "$TIMESTAMP" \
|
||||
--arg rolle "schnippsi" \
|
||||
--arg usage "$(jq -c '.usage' "$TMP_RESPONSE")" \
|
||||
'{zeit: $zeit, rolle: $rolle, usage: $usage}' >> "$LOG_FILE"
|
||||
fi
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
QUESTION="$*"
|
||||
MODEL="openai/gpt-3.5-turbo"
|
||||
API_KEY="${OPENROUTER_API_KEY}"
|
||||
|
||||
LOGDIR="$HOME/.tobi_logs"
|
||||
mkdir -p "$LOGDIR"
|
||||
|
||||
HISTORY_FILE="$LOGDIR/tobi_history.json"
|
||||
TMP_REQUEST="$LOGDIR/tobi_request.json"
|
||||
TMP_RESPONSE="$LOGDIR/tobi_response.json"
|
||||
LOG_FILE="$LOGDIR/token_log.json"
|
||||
|
||||
[ ! -f "$HISTORY_FILE" ] && echo "[]" > "$HISTORY_FILE"
|
||||
[ ! -f "$LOG_FILE" ] && echo "[]" > "$LOG_FILE"
|
||||
|
||||
echo "🌳 Tobi fragt über OpenRouter: $QUESTION"
|
||||
|
||||
if [ -z "$API_KEY" ]; then
|
||||
echo "❗ Kein API-Key gesetzt. Bitte export OPENROUTER_API_KEY=... setzen"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
jq -n \
|
||||
--arg model "$MODEL" \
|
||||
--arg system_prompt "Du bist CapaciTobi – ein quirliges Eichhörnchen, das Kindern Strom, Spannung, Widerstand und Kapazität erklärt." \
|
||||
--arg user "$QUESTION" \
|
||||
'{"model": $model, "temperature": 0.5, "messages": [{"role": "system", "content": $system_prompt}, {"role": "user", "content": $user}]}' > "$TMP_REQUEST"
|
||||
|
||||
curl -s https://openrouter.ai/api/v1/chat/completions \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @"$TMP_REQUEST" > "$TMP_RESPONSE"
|
||||
|
||||
RESPONSE_TEXT=$(jq -r '.choices[0].message.content // empty' "$TMP_RESPONSE")
|
||||
|
||||
if [[ -z "$RESPONSE_TEXT" ]]; then
|
||||
echo "🚫 Keine Antwort vom Modell erhalten."
|
||||
exit 1
|
||||
else
|
||||
echo -e "$RESPONSE_TEXT"
|
||||
jq -n --arg role "assistant" --arg content "$RESPONSE_TEXT" \
|
||||
'{"role": $role, "content": $content}' > "$LOGDIR/new_entry.json"
|
||||
jq -s '.[0] + [.[1]]' "$HISTORY_FILE" "$LOGDIR/new_entry.json" > "$LOGDIR/new_history.json" && \
|
||||
cp "$LOGDIR/new_history.json" "$HISTORY_FILE" && rm "$LOGDIR/new_history.json"
|
||||
fi
|
||||
|
||||
# Token Logging
|
||||
if jq -e '.usage' "$TMP_RESPONSE" > /dev/null; then
|
||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
jq -n \
|
||||
--arg zeit "$TIMESTAMP" \
|
||||
--arg rolle "tobi" \
|
||||
--arg usage "$(jq -c '.usage' "$TMP_RESPONSE")" \
|
||||
'{zeit: $zeit, rolle: $rolle, usage: $usage}' >> "$LOG_FILE"
|
||||
fi
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "🔧 Repariere Crumbforest Token-Logs mit eingebetteten JSON-Strings …"
|
||||
|
||||
LOG_DIRS=(
|
||||
"/home/zero/.bugsy_logs"
|
||||
"/home/zero/.deepbit_logs"
|
||||
"/home/zero/.dumbo_logs"
|
||||
"/home/zero/.funkfox_logs"
|
||||
"/home/zero/.pepper_logs"
|
||||
"/home/zero/.schnecki_logs"
|
||||
"/home/zero/.schnippsi_logs"
|
||||
"/home/zero/.schraubaer_logs"
|
||||
"/home/zero/.snake_logs"
|
||||
"/home/zero/.taube_logs"
|
||||
"/home/zero/.templatus_logs"
|
||||
"/home/zero/.tobi_logs"
|
||||
)
|
||||
|
||||
for dir in "${LOG_DIRS[@]}"; do
|
||||
FILE="$dir/token_log.json"
|
||||
if [ -f "$FILE" ]; then
|
||||
echo "🔍 Prüfe $FILE …"
|
||||
TMP="$FILE.fixed"
|
||||
|
||||
# Filter & reparieren Zeile für Zeile
|
||||
jq -c '.' "$FILE" 2>/dev/null | while read -r line; do
|
||||
usage_raw=$(echo "$line" | jq -r '.usage')
|
||||
if [[ "$usage_raw" =~ ^\{.*\}$ ]]; then
|
||||
# usage ist korrektes Objekt – direkt übernehmen
|
||||
echo "$line" >> "$TMP"
|
||||
else
|
||||
# usage ist String – versuche zu reparieren
|
||||
usage_fixed=$(echo "$usage_raw" | jq '.' 2>/dev/null)
|
||||
if [ $? -eq 0 ]; then
|
||||
zeit=$(echo "$line" | jq -r '.zeit')
|
||||
rolle=$(echo "$line" | jq -r '.rolle')
|
||||
jq -n \
|
||||
--arg zeit "$zeit" \
|
||||
--arg rolle "$rolle" \
|
||||
--argjson usage "$usage_fixed" \
|
||||
'{zeit: $zeit, rolle: $rolle, usage: $usage}' >> "$TMP"
|
||||
else
|
||||
echo "⚠️ Ungültige Zeile übersprungen in $FILE:"
|
||||
echo "$line"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Nur ersetzen, wenn wir etwas geschrieben haben
|
||||
if [ -s "$TMP" ]; then
|
||||
mv "$TMP" "$FILE"
|
||||
echo "✅ Repariert: $FILE"
|
||||
else
|
||||
echo "ℹ️ Keine gültigen Einträge in $FILE"
|
||||
rm -f "$TMP"
|
||||
fi
|
||||
else
|
||||
echo "❌ Datei nicht gefunden: $FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "🎉 Alle Token-Logs geprüft und repariert (sofern nötig)."
|
||||
@@ -3,7 +3,18 @@
|
||||
# Source this file to make all AI characters available as commands
|
||||
|
||||
# Determine the repo root directory (lib/ is inside repo root)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# Support both bash and zsh
|
||||
if [[ -n "$BASH_VERSION" ]]; then
|
||||
# Bash: use BASH_SOURCE
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
elif [[ -n "$ZSH_VERSION" ]]; then
|
||||
# Zsh: use ${(%):-%x} to get the script path
|
||||
SCRIPT_DIR="$(cd "$(dirname "${(%):-%x}")" && pwd)"
|
||||
else
|
||||
# Fallback: use $0 (may not work when sourced)
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
fi
|
||||
|
||||
WALDWAECHTER_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
ROLES_DIR="${WALDWAECHTER_DIR}/crumbforest_roles"
|
||||
|
||||
@@ -108,21 +119,402 @@ function asciimonster() {
|
||||
"${ROLES_DIR}/asciimonster_zero.sh" "$@"
|
||||
}
|
||||
|
||||
# === CREW COMMANDS ===
|
||||
|
||||
# 📊 crew_tokens - Token-Verbrauch aller Waldwächter
|
||||
function crew_tokens() {
|
||||
# Force C locale for consistent number formatting
|
||||
export LC_NUMERIC=C
|
||||
|
||||
echo "📊 CrumbCrew Token-Verbrauch"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
local total_tokens=0
|
||||
local total_cost=0
|
||||
local crew_count=0
|
||||
|
||||
# Alle token_log.json Dateien finden und auswerten
|
||||
for token_file in "${CRUMB_LOGS_DIR}"/*/token_log.json; do
|
||||
if [[ -f "$token_file" ]]; then
|
||||
local character=$(basename "$(dirname "$token_file")")
|
||||
|
||||
# Tokens und Kosten aus JSON extrahieren (skip erste Zeile falls es [] ist)
|
||||
local char_tokens=$(grep -v '^\[\]$' "$token_file" | jq -s 'map(.usage.total_tokens // 0) | add // 0' 2>/dev/null || echo 0)
|
||||
local char_cost=$(grep -v '^\[\]$' "$token_file" | jq -s 'map(.usage.cost // 0) | add // 0' 2>/dev/null || echo 0)
|
||||
|
||||
if [[ $char_tokens -gt 0 ]]; then
|
||||
printf " %-15s %8d tokens (~\$%.6f)\n" "$character:" "$char_tokens" "$char_cost"
|
||||
total_tokens=$((total_tokens + char_tokens))
|
||||
total_cost=$(echo "$total_cost + $char_cost" | bc -l)
|
||||
crew_count=$((crew_count + 1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
if [[ $total_tokens -gt 0 ]]; then
|
||||
printf " Gesamt: %d Tokens (~\$%.6f)\n" "$total_tokens" "$total_cost"
|
||||
printf " %d Waldwächter aktiv 🌲\n" "$crew_count"
|
||||
else
|
||||
echo " Gesamt: 0 Tokens"
|
||||
echo " Jede Frage ist wertvoll 🌲"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Restore locale
|
||||
unset LC_NUMERIC
|
||||
}
|
||||
|
||||
# 📋 crew_status - Status aller Waldwächter
|
||||
function crew_status() {
|
||||
echo "📋 CrumbCrew Status (17 Waldwächter)"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
local chars=("mayaeule" "deepbit" "bugsy" "schnippsi" "templatus" "tobi" "schraubaer" "schnecki" "dumbosql" "funkfox" "taichitaube" "snakepy" "pepperphp" "crabbyrust" "spider" "vektor" "asciimonster")
|
||||
|
||||
for char in "${chars[@]}"; do
|
||||
local token_file="${CRUMB_LOGS_DIR}/${char}/token_log.json"
|
||||
if [[ -f "$token_file" ]] && grep -q -v '^\[\]$' "$token_file" 2>/dev/null; then
|
||||
local last_used=$(grep -v '^\[\]$' "$token_file" | tail -1 | jq -r '.zeit // "unknown"' 2>/dev/null || echo "unknown")
|
||||
printf " ✅ %-15s (zuletzt: %s)\n" "$char" "$last_used"
|
||||
else
|
||||
printf " ⚪ %-15s (noch nicht genutzt)\n" "$char"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
}
|
||||
|
||||
# 🧠 crew_memory - Erinnerungen durchsuchen
|
||||
function crew_memory() {
|
||||
local query="$1"
|
||||
|
||||
if [[ -z "$query" ]]; then
|
||||
echo "🧠 Crew Memory - Log-basiertes Gedächtnis"
|
||||
echo ""
|
||||
echo "Verwendung: crew_memory <suchbegriff>"
|
||||
echo ""
|
||||
echo "Beispiele:"
|
||||
echo " crew_memory 'LED'"
|
||||
echo " crew_memory 'sensor'"
|
||||
echo ""
|
||||
echo "Durchsucht alle Waldwächter-Logs nach dem Begriff."
|
||||
return
|
||||
fi
|
||||
|
||||
echo "🧠 Suche nach: '$query'"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
local found=0
|
||||
for history_file in "${CRUMB_LOGS_DIR}"/*/$(basename "${history_file}" | grep '_history.json$'); do
|
||||
if [[ -f "$history_file" ]]; then
|
||||
local character=$(basename "$(dirname "$history_file")")
|
||||
local matches=$(grep -i "$query" "$history_file" 2>/dev/null)
|
||||
|
||||
if [[ -n "$matches" ]]; then
|
||||
echo ""
|
||||
echo "📍 $character:"
|
||||
echo "$matches" | jq -r '.content' 2>/dev/null | head -3
|
||||
found=$((found + 1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
if [[ $found -gt 0 ]]; then
|
||||
echo " Gefunden in $found Waldwächter-Logs"
|
||||
else
|
||||
echo " Keine Treffer gefunden"
|
||||
fi
|
||||
}
|
||||
|
||||
# 🩺 crew_doctor - System-Diagnose
|
||||
function crew_doctor() {
|
||||
echo "🩺 CrumbCrew System-Diagnose"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
local issues=0
|
||||
|
||||
# 1. Waldwaechter.sh Version Check
|
||||
echo "📋 Checking waldwaechter.sh..."
|
||||
local lib_file="${WALDWAECHTER_DIR}/lib/waldwaechter.sh"
|
||||
if [[ -f "$lib_file" ]]; then
|
||||
local lib_mtime=$(stat -f "%m" "$lib_file" 2>/dev/null || stat -c "%Y" "$lib_file" 2>/dev/null)
|
||||
local loaded_check_var="WALDWAECHTER_LOADED_${lib_mtime}"
|
||||
|
||||
# Indirect variable expansion (bash: ${!var}, zsh: ${(P)var})
|
||||
local loaded_value=""
|
||||
if [[ -n "$BASH_VERSION" ]]; then
|
||||
loaded_value="${!loaded_check_var}"
|
||||
elif [[ -n "$ZSH_VERSION" ]]; then
|
||||
loaded_value="${(P)loaded_check_var}"
|
||||
fi
|
||||
|
||||
if [[ -z "$loaded_value" ]]; then
|
||||
echo " ⚠️ waldwaechter.sh wurde aktualisiert!"
|
||||
echo " → Bitte neu laden: source lib/waldwaechter.sh"
|
||||
issues=$((issues + 1))
|
||||
else
|
||||
echo " ✅ waldwaechter.sh ist aktuell"
|
||||
fi
|
||||
else
|
||||
echo " ❌ waldwaechter.sh nicht gefunden!"
|
||||
issues=$((issues + 1))
|
||||
fi
|
||||
|
||||
# 2. CRUMB_LOGS_DIR Check
|
||||
echo ""
|
||||
echo "📂 Checking CRUMB_LOGS_DIR..."
|
||||
if [[ -d "$CRUMB_LOGS_DIR" ]]; then
|
||||
echo " ✅ $CRUMB_LOGS_DIR existiert"
|
||||
|
||||
# Prüfe ob es der richtige Pfad ist (sollte im Repo sein)
|
||||
if [[ "$CRUMB_LOGS_DIR" == *"CF_Zero_V1/logs"* ]]; then
|
||||
echo " ✅ Pfad sieht korrekt aus"
|
||||
else
|
||||
echo " ⚠️ Pfad sieht ungewöhnlich aus: $CRUMB_LOGS_DIR"
|
||||
echo " Erwartet: .../CF_Zero_V1/logs"
|
||||
issues=$((issues + 1))
|
||||
fi
|
||||
else
|
||||
echo " ❌ $CRUMB_LOGS_DIR existiert nicht!"
|
||||
issues=$((issues + 1))
|
||||
fi
|
||||
|
||||
# 3. Character Scripts Check
|
||||
echo ""
|
||||
echo "🌲 Checking 17 Waldwächter Scripts..."
|
||||
local expected_chars=("mayaeule" "deepbit" "bugsy" "schnippsi" "templatus" "tobi" "schraubaer" "schnecki" "dumbosql" "funkfox" "taichitaube" "snakepy" "pepperphp" "crabbyrust" "spider" "vektor" "asciimonster")
|
||||
local missing_count=0
|
||||
|
||||
for char in "${expected_chars[@]}"; do
|
||||
local script="${ROLES_DIR}/${char}_zero.sh"
|
||||
[[ "$char" == "schraubaer" ]] && script="${ROLES_DIR}/schraubaer_zero_final.sh"
|
||||
|
||||
if [[ ! -f "$script" ]]; then
|
||||
echo " ❌ $char Script fehlt!"
|
||||
missing_count=$((missing_count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $missing_count -eq 0 ]]; then
|
||||
echo " ✅ Alle 17 Waldwächter Scripts vorhanden"
|
||||
else
|
||||
echo " ❌ $missing_count Scripts fehlen!"
|
||||
issues=$((issues + 1))
|
||||
fi
|
||||
|
||||
# 4. Dependencies Check
|
||||
echo ""
|
||||
echo "🔧 Checking Dependencies..."
|
||||
local deps_ok=0
|
||||
for cmd in jq bc curl; do
|
||||
if command -v "$cmd" &> /dev/null; then
|
||||
echo " ✅ $cmd verfügbar"
|
||||
deps_ok=$((deps_ok + 1))
|
||||
else
|
||||
echo " ❌ $cmd fehlt!"
|
||||
issues=$((issues + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# 5. Token-Logging Check
|
||||
echo ""
|
||||
echo "📊 Checking Token-Logging..."
|
||||
local scripts_with_logging=$(grep -l "token_log.json" "${ROLES_DIR}"/*.sh 2>/dev/null | wc -l | tr -d ' ')
|
||||
if [[ $scripts_with_logging -eq 17 ]]; then
|
||||
echo " ✅ Alle 17 Scripts haben Token-Logging"
|
||||
else
|
||||
echo " ⚠️ Nur $scripts_with_logging/17 Scripts haben Token-Logging"
|
||||
issues=$((issues + 1))
|
||||
fi
|
||||
|
||||
# 6. API Key Check
|
||||
echo ""
|
||||
echo "🔑 Checking API Key..."
|
||||
if [[ -n "$OPENROUTER_API_KEY" ]]; then
|
||||
echo " ✅ OPENROUTER_API_KEY gesetzt"
|
||||
else
|
||||
echo " ⚠️ OPENROUTER_API_KEY nicht gesetzt"
|
||||
echo " → Bitte in .env konfigurieren"
|
||||
issues=$((issues + 1))
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
if [[ $issues -eq 0 ]]; then
|
||||
echo " ✅ Alle Checks bestanden! System gesund 💚"
|
||||
else
|
||||
echo " ⚠️ $issues Problem(e) gefunden"
|
||||
echo " Bitte oben genannte Punkte beheben"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
}
|
||||
|
||||
# 🔍 crew_syntax - Syntax Check aller Scripts
|
||||
function crew_syntax() {
|
||||
echo "🔍 CrumbCrew Syntax Check"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
local errors=0
|
||||
local checked=0
|
||||
|
||||
echo "Prüfe Waldwächter Scripts..."
|
||||
echo ""
|
||||
|
||||
# Check lib/waldwaechter.sh
|
||||
if bash -n "${WALDWAECHTER_DIR}/lib/waldwaechter.sh" 2>/dev/null; then
|
||||
echo " ✅ lib/waldwaechter.sh"
|
||||
else
|
||||
echo " ❌ lib/waldwaechter.sh - SYNTAX ERROR!"
|
||||
bash -n "${WALDWAECHTER_DIR}/lib/waldwaechter.sh"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
checked=$((checked + 1))
|
||||
|
||||
# Check all character scripts
|
||||
for script in "${ROLES_DIR}"/*_zero*.sh; do
|
||||
if [[ -f "$script" ]]; then
|
||||
local name=$(basename "$script")
|
||||
if bash -n "$script" 2>/dev/null; then
|
||||
echo " ✅ $name"
|
||||
else
|
||||
echo " ❌ $name - SYNTAX ERROR!"
|
||||
echo ""
|
||||
bash -n "$script" 2>&1 | sed 's/^/ /'
|
||||
echo ""
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
checked=$((checked + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check mission scripts (optional)
|
||||
echo ""
|
||||
echo "Prüfe Mission Scripts..."
|
||||
echo ""
|
||||
|
||||
for script in "${WALDWAECHTER_DIR}/missions"/*/*.sh; do
|
||||
if [[ -f "$script" ]]; then
|
||||
local name=$(basename "$script")
|
||||
if bash -n "$script" 2>/dev/null; then
|
||||
echo " ✅ $name"
|
||||
else
|
||||
echo " ❌ $name - SYNTAX ERROR!"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
checked=$((checked + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
if [[ $errors -eq 0 ]]; then
|
||||
echo " ✅ $checked Scripts geprüft - Keine Syntax-Fehler! 💚"
|
||||
else
|
||||
echo " ❌ $errors von $checked Scripts haben Syntax-Fehler"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
}
|
||||
|
||||
# ❓ crew_help - Hilfe für Crew-Befehle
|
||||
function crew_help() {
|
||||
cat << 'EOF'
|
||||
🌲 CrumbCrew Befehle
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Crew-Verwaltung:
|
||||
crew_help Diese Hilfe anzeigen
|
||||
crew_status Status aller 17 Waldwächter
|
||||
crew_tokens Token-Verbrauch ALLER Charaktere
|
||||
crew_memory Erinnerungen durchsuchen
|
||||
crew_doctor System-Diagnose (Version, Pfade, Dependencies)
|
||||
crew_syntax Syntax Check aller Scripts
|
||||
|
||||
Einzelne Waldwächter:
|
||||
|
||||
🔺 Das Dreieck (Foundation):
|
||||
dumbosql SQL & Datenstrukturen
|
||||
funkfox Bash im Beat
|
||||
taichitaube Balance & Spirale
|
||||
|
||||
🔧 Hardware-Team:
|
||||
tobi Elektronik-Theorie (CapaciTobi)
|
||||
schnecki Elektronik-Basteln
|
||||
schraubaer Mechanik & Werkzeug
|
||||
|
||||
💻 Code-Team:
|
||||
snakepy Python Guide
|
||||
pepperphp Structure Mentor
|
||||
crabbyrust Security Guardian
|
||||
spider Network Feeler
|
||||
|
||||
🎨 UI-Team:
|
||||
schnippsi CSS & Styling
|
||||
templatus Template-Master
|
||||
asciimonster Terminal Artist
|
||||
|
||||
🧭 System-Team:
|
||||
mayaeule Weise Eule
|
||||
deepbit Bash-Erklärer
|
||||
bugsy Debugging-Helfer
|
||||
vektor Navigation
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Beispiele:
|
||||
funkfox "Erkläre mir Pipes im Flow!"
|
||||
crew_tokens
|
||||
crew_memory "LED"
|
||||
|
||||
"Was kostet die Frage eines Kindes?" 💚
|
||||
EOF
|
||||
}
|
||||
|
||||
# Export functions so they're available in subshells
|
||||
export -f mayaeule
|
||||
export -f deepbit
|
||||
export -f bugsy
|
||||
export -f schnippsi
|
||||
export -f templatus
|
||||
export -f tobi
|
||||
export -f schraubaer
|
||||
export -f schnecki
|
||||
export -f dumbosql
|
||||
export -f funkfox
|
||||
export -f taichitaube
|
||||
export -f snakepy
|
||||
export -f pepperphp
|
||||
export -f crabbyrust
|
||||
export -f spider
|
||||
export -f vektor
|
||||
export -f asciimonster
|
||||
# Note: export -f works in bash, but not in zsh
|
||||
# In zsh, functions are automatically available in the current shell
|
||||
# For bash compatibility, we still do export -f, but it's optional in zsh
|
||||
if [[ -n "$BASH_VERSION" ]]; then
|
||||
# Bash: use export -f
|
||||
export -f mayaeule
|
||||
export -f deepbit
|
||||
export -f bugsy
|
||||
export -f schnippsi
|
||||
export -f templatus
|
||||
export -f tobi
|
||||
export -f schraubaer
|
||||
export -f schnecki
|
||||
export -f dumbosql
|
||||
export -f funkfox
|
||||
export -f taichitaube
|
||||
export -f snakepy
|
||||
export -f pepperphp
|
||||
export -f crabbyrust
|
||||
export -f spider
|
||||
export -f vektor
|
||||
export -f asciimonster
|
||||
export -f crew_tokens
|
||||
export -f crew_status
|
||||
export -f crew_memory
|
||||
export -f crew_doctor
|
||||
export -f crew_syntax
|
||||
export -f crew_help
|
||||
fi
|
||||
# In zsh, functions are available without export -f
|
||||
|
||||
# Set version marker for crew_doctor to detect reloads
|
||||
if [[ -f "${WALDWAECHTER_DIR}/lib/waldwaechter.sh" ]]; then
|
||||
WALDWAECHTER_LIB_MTIME=$(stat -f "%m" "${WALDWAECHTER_DIR}/lib/waldwaechter.sh" 2>/dev/null || stat -c "%Y" "${WALDWAECHTER_DIR}/lib/waldwaechter.sh" 2>/dev/null)
|
||||
export "WALDWAECHTER_LOADED_${WALDWAECHTER_LIB_MTIME}=1"
|
||||
fi
|
||||
|
||||
65
missions/robots/mond_maschine.meta.json
Normal file
65
missions/robots/mond_maschine.meta.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"icon": "🌙",
|
||||
"title": "Mond Maschine - Rainbow Predictor",
|
||||
"description": "Baue eine Maschine die Regenbogen vorhersagt! Mit Mond-Phasen, Wetter-Daten & Computer Vision.",
|
||||
"category": "robots",
|
||||
"difficulty": "advanced",
|
||||
"duration_minutes": 35,
|
||||
"requires_ai": true,
|
||||
"enabled": true,
|
||||
"author": "Crumbforest Team",
|
||||
"version": "1.0",
|
||||
"crew_involved": [
|
||||
"mayaeule",
|
||||
"dumbosql",
|
||||
"funkfox",
|
||||
"taichitaube",
|
||||
"tobi",
|
||||
"schnecki",
|
||||
"schraubaer",
|
||||
"snakepy",
|
||||
"pepperphp",
|
||||
"spider",
|
||||
"crabbyrust",
|
||||
"vektor",
|
||||
"asciimonster"
|
||||
],
|
||||
"tags": [
|
||||
"computer-vision",
|
||||
"opencv",
|
||||
"weather-api",
|
||||
"moon-phases",
|
||||
"machine-learning",
|
||||
"prediction",
|
||||
"camera",
|
||||
"image-processing",
|
||||
"astronomy"
|
||||
],
|
||||
"philosophy": "Vorhersagen heißt verstehen - Die Natur durch Code begreifen",
|
||||
"learning_objectives": [
|
||||
"Computer Vision Grundlagen (OpenCV)",
|
||||
"Bildverarbeitung (HSV-Farbraum für Regenbogen)",
|
||||
"Wetter-API Integration",
|
||||
"Mondphasen-Berechnung",
|
||||
"Pattern Recognition & Machine Learning",
|
||||
"Zeitreihen-Analyse",
|
||||
"Kamera-Setup & Bilderfassung",
|
||||
"Vorhersage-Algorithmen",
|
||||
"Data Visualization"
|
||||
],
|
||||
"prerequisites": [
|
||||
"Python Grundkenntnisse",
|
||||
"Interesse an Computer Vision",
|
||||
"Optional: Kamera (Webcam oder Raspi Cam)"
|
||||
],
|
||||
"hardware_needed": [
|
||||
"Kamera (USB Webcam oder Raspberry Pi Camera)",
|
||||
"Optional: Wetterfester Gehäuse für Outdoor",
|
||||
"Raspberry Pi oder Computer mit Python"
|
||||
],
|
||||
"references": [
|
||||
"OpenCV Documentation",
|
||||
"Weather API (OpenWeatherMap, Weatherstack)",
|
||||
"Astronomy libraries (ephem, skyfield)"
|
||||
]
|
||||
}
|
||||
432
missions/robots/mond_maschine.sh
Executable file
432
missions/robots/mond_maschine.sh
Executable file
@@ -0,0 +1,432 @@
|
||||
#!/bin/bash
|
||||
# 🌙 Mond Maschine - Rainbow Predictor
|
||||
# "Vorhersagen heißt verstehen - Die Natur durch Code begreifen"
|
||||
|
||||
# Waldwächter laden
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/../../lib/waldwaechter.sh"
|
||||
|
||||
# === INTRO ===
|
||||
|
||||
clear
|
||||
cat << "EOF"
|
||||
🌙🌈 MOND MASCHINE - RAINBOW PREDICTOR 🌈🌙
|
||||
|
||||
"Vorhersagen heißt verstehen"
|
||||
|
||||
Eine Maschine die weiß, wann Regenbogen erscheinen!
|
||||
|
||||
Mond-Phasen 🌙 + Wetter 🌦️ + Computer Vision 📸
|
||||
= Rainbow Prediction! 🌈
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
sleep 2
|
||||
|
||||
# === PHASE 1: DIE GROSSE VISION ===
|
||||
|
||||
echo "🦉 Maya-Eule erscheint aus der Dämmerung..."
|
||||
echo ""
|
||||
mayaeule "Ein Krümel fragte: 'Kann eine Maschine wissen, wann Regenbogen kommen?' Was ist schöner - Regenbogen zu zählen, oder zu wissen wann sie erscheinen?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
read -p "🌙 Was ist DEINE Vision? Warum eine Mond Maschine bauen? " USER_VISION
|
||||
echo ""
|
||||
echo "💚 Deine Vision: $USER_VISION"
|
||||
echo ""
|
||||
|
||||
# === PHASE 2: DAS DREIECK PLANT DIE DATEN ===
|
||||
|
||||
read -p "🔺 Drücke Enter für das Dreieck..." -r
|
||||
clear
|
||||
|
||||
echo "🔺 Das Dreieck versammelt sich unter dem Mond..."
|
||||
echo ""
|
||||
sleep 1
|
||||
|
||||
echo "🐘 DumboSQL: Die Daten-Struktur"
|
||||
dumbosql "Wir brauchen eine Datenbank für: Mond-Phasen (Datum, Phase, Helligkeit), Wetter-Daten (Temperatur, Luftfeuchtigkeit, Regen), und Regenbogen-Sichtungen (Datum, Uhrzeit, Foto). Wie strukturieren wir das?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
echo "🦊 FunkFox: Der Daten-Flow"
|
||||
funkfox "Yo, Daten fließen rein im Flow: Mond-API → Wetter-API → Kamera → Prediction! Wie bauen wir die Pipeline mit Bash & Python?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
echo "🕊️ Taichi Taube: Die Balance"
|
||||
taichitaube "Zwischen Vorhersage und Beobachtung liegt die Wahrheit. Wie finden wir Balance zwischen Algorithmus und Realität?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# === PHASE 3: HARDWARE - DIE KAMERA ===
|
||||
|
||||
read -p "📸 Drücke Enter für Hardware..." -r
|
||||
clear
|
||||
|
||||
echo "🔧 HARDWARE-TEAM: Die Kamera"
|
||||
echo ""
|
||||
|
||||
read -p "📸 Welche Kamera nutzt du? (USB Webcam / Raspberry Pi Camera / Smartphone / andere) " CAMERA_TYPE
|
||||
echo ""
|
||||
echo "📸 Deine Kamera: $CAMERA_TYPE"
|
||||
echo ""
|
||||
|
||||
echo "🐿️ CapaciTobi rechnet..."
|
||||
tobi "Eine $CAMERA_TYPE für Regenbogen-Fotos - wie viel Power braucht sie? Und wie lange Akku-Laufzeit für Outdoor-Montage?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
echo "🐌 Schnecki: Verkabelung"
|
||||
schnecki "Die Kamera muss mit dem Computer verbunden werden. Bei $CAMERA_TYPE: USB? CSI? WiFi? Ich zeige dir die Kabel-Wege."
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
echo "🐻 Schraubbär: Wetterfestes Gehäuse"
|
||||
schraubaer "Outdoor-Kamera braucht Schutz! Regen, Wind, Sonne - das Gehäuse muss robust sein. Acryl-Box? 3D-Druck? Gummi-Dichtung?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# === PHASE 4: CODE - COMPUTER VISION ===
|
||||
|
||||
read -p "💻 Drücke Enter für Computer Vision..." -r
|
||||
clear
|
||||
|
||||
echo "💻 CODE-TEAM: Computer Vision mit OpenCV"
|
||||
echo ""
|
||||
|
||||
cat << "EOF"
|
||||
📚 Computer Vision für Regenbogen:
|
||||
|
||||
1. HSV-Farbraum (Hue, Saturation, Value)
|
||||
- Regenbogen = spezifische Farbverläufe
|
||||
- Rot-Orange-Gelb-Grün-Blau-Violett
|
||||
|
||||
2. Kontur-Erkennung
|
||||
- Bogenform detektieren
|
||||
- Horizontal + Gekrümmt
|
||||
|
||||
3. Helligkeit & Kontrast
|
||||
- Nach Regen + Sonne = Idealbedingung
|
||||
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
echo "🐍 SnakePy: OpenCV Code"
|
||||
snakepy "Zeig mir Python-Code für: (1) Kamera-Capture mit OpenCV, (2) HSV-Konvertierung, (3) Regenbogen-Farben detektieren. Welche Bibliotheken? cv2, numpy, welche noch?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# === PHASE 5: WETTER & MOND DATEN ===
|
||||
|
||||
read -p "🌦️ Drücke Enter für Wetter & Mond..." -r
|
||||
clear
|
||||
|
||||
echo "🌦️🌙 DATEN-INTEGRATION"
|
||||
echo ""
|
||||
|
||||
cat << "EOF"
|
||||
📡 Daten-Quellen:
|
||||
|
||||
Wetter-APIs:
|
||||
- OpenWeatherMap (free tier)
|
||||
- Weatherstack
|
||||
- NOAA (US Government)
|
||||
|
||||
Mond-Phasen:
|
||||
- Python: ephem / skyfield
|
||||
- Berechnung: Lunation Cycle (29.5 Tage)
|
||||
|
||||
Ideale Regenbogen-Bedingungen:
|
||||
- Nach Regen ☔
|
||||
- Sonne scheint ☀️
|
||||
- Nachmittags (tieferer Sonnenstand)
|
||||
- Luftfeuchtigkeit > 60%
|
||||
- Vollmond Nacht? (Mondregenbogen!)
|
||||
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
echo "🕷️ Spider: API Integration"
|
||||
spider "Ich spüre die Daten-Verbindungen: Wetter-API mit requests, Mond-Berechnung mit ephem. Wie oft pollen wir? Stündlich? Alle 10 Minuten?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
echo "🐘 DumboSQL: Historische Daten"
|
||||
dumbosql "Ich erinnere mich an alle Regenbogen-Sichtungen. Mit historischen Daten können wir Muster erkennen: Wann erscheinen Regenbogen hier am häufigsten?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# === PHASE 6: DER VORHERSAGE-ALGORITHMUS ===
|
||||
|
||||
read -p "🔮 Drücke Enter für Vorhersage..." -r
|
||||
clear
|
||||
|
||||
echo "🔮 VORHERSAGE-ALGORITHMUS"
|
||||
echo ""
|
||||
|
||||
cat << "EOF"
|
||||
🧠 Machine Learning Light:
|
||||
|
||||
1. Feature Engineering:
|
||||
- Mondphase (0-1)
|
||||
- Wetter-Score (Regen + Sonne)
|
||||
- Tageszeit (14-18 Uhr = hoch)
|
||||
- Jahreszeit (Frühling/Herbst = hoch)
|
||||
- Luftfeuchtigkeit
|
||||
|
||||
2. Scoring-System:
|
||||
Score = moon_weight * moon_phase
|
||||
+ weather_weight * weather_score
|
||||
+ time_weight * time_score
|
||||
+ humidity_weight * humidity
|
||||
|
||||
3. Threshold:
|
||||
Score > 0.7 → "🌈 Hohe Wahrscheinlichkeit!"
|
||||
Score 0.4-0.7 → "🤔 Möglich"
|
||||
Score < 0.4 → "❌ Unwahrscheinlich"
|
||||
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
echo "🧭 Vektor: Zeit & Ort"
|
||||
vektor "Navigation durch Zeit und Raum: Dein Standort bestimmt den Sonnenwinkel. Deine Zeitzone die Regenzeit. Wo bist du? Wann ist hier Regenbogen-Saison?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
echo "🦀 CrabbyRust: Sichere Daten"
|
||||
crabbyrust "Wetter-Daten von extern, Kamera-Bilder lokal - wie halten wir das sicher? Validierung, Error-Handling, keine Race Conditions bei Multi-Threading?"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# === PHASE 7: VISUALISIERUNG ===
|
||||
|
||||
read -p "🎨 Drücke Enter für Visualisierung..." -r
|
||||
clear
|
||||
|
||||
echo "🎨 VISUALISIERUNG & UI"
|
||||
echo ""
|
||||
|
||||
echo "👾 ASCII-Monster malt den Mond..."
|
||||
asciimonster "Erstelle ASCII-Art für Mond-Phasen: Neumond 🌑, Zunehmend 🌒🌓🌔, Vollmond 🌕, Abnehmend 🌖🌗🌘. Für das Terminal!"
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
read -p "📊 Willst du ein Web-Dashboard? (j/n) " WANT_DASHBOARD
|
||||
|
||||
if [[ "$WANT_DASHBOARD" == "j" ]]; then
|
||||
echo ""
|
||||
echo "🧓 PepperPHP: Dashboard-Rezept"
|
||||
pepperphp "Ein Dashboard mit: Live-Kamera-Feed, Mond-Phase-Anzeige, Wetter-Daten, Vorhersage-Score, Historische Sichtungen. Wie backen wir das mit Flask/FastAPI?"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# === PHASE 8: DER MONDREGENBOGEN ===
|
||||
|
||||
read -p "🌙 Drücke Enter für das Finale..." -r
|
||||
clear
|
||||
|
||||
echo "🦉 Maya-Eule: Die Legende vom Mondregenbogen"
|
||||
echo ""
|
||||
|
||||
cat << "EOF"
|
||||
🌙🌈 MONDREGENBOGEN (Lunar Rainbow)
|
||||
|
||||
Wusstest du?
|
||||
Bei Vollmond kann es nachts Regenbogen geben!
|
||||
|
||||
Bedingungen:
|
||||
- Vollmond 🌕
|
||||
- Nach Regen (Wassertropfen in Luft)
|
||||
- Dunkle Nacht
|
||||
- Mond tief am Horizont (ca. 42° Winkel)
|
||||
|
||||
Die Mond Maschine könnte auch DAS vorhersagen!
|
||||
Mondregenbogen sind selten und magisch. 🌙✨
|
||||
|
||||
Orte mit häufigen Mondregenbogen:
|
||||
- Wasserfälle (z.B. Yosemite, Victoria Falls)
|
||||
- Küsten nach Sturm
|
||||
- Gebirge mit Nebel
|
||||
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
mayaeule "Die Mond Maschine verbindet Wissenschaft und Poesie. Vorhersagen heißt verstehen - und verstehen heißt staunen."
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# === ABSCHLUSS ===
|
||||
|
||||
read -p "🎓 Drücke Enter für Abschluss..." -r
|
||||
clear
|
||||
|
||||
cat << "EOF"
|
||||
🌙🎓 MOND MASCHINE: GEPLANT! 🎓🌙
|
||||
|
||||
Du hast gelernt:
|
||||
|
||||
📸 Computer Vision: OpenCV & Bildverarbeitung
|
||||
🌦️ API Integration: Wetter & Astronomie-Daten
|
||||
🧠 Machine Learning: Feature Engineering & Scoring
|
||||
🌙 Astronomie: Mond-Phasen & Sonnenwinkel
|
||||
🔮 Vorhersage: Algorithmen & Threshold-Systeme
|
||||
📊 Visualisierung: Dashboard & Terminal-Art
|
||||
🔐 Data Safety: Validierung & Error-Handling
|
||||
|
||||
"Vorhersagen heißt verstehen"
|
||||
|
||||
Mit dieser Mond Maschine kannst du die Natur
|
||||
durch Code begreifen! 🌈✨
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# === LOG ===
|
||||
|
||||
LOGDIR="${CRUMB_LOGS_DIR:-$HOME/.crumbrobots_logs}/missions"
|
||||
mkdir -p "$LOGDIR"
|
||||
TIMESTAMP=$(date -Iseconds)
|
||||
|
||||
cat > "$LOGDIR/mond_maschine_${TIMESTAMP}.json" << EOF
|
||||
{
|
||||
"mission": "mond_maschine",
|
||||
"timestamp": "$TIMESTAMP",
|
||||
"vision": "$USER_VISION",
|
||||
"camera": "$CAMERA_TYPE",
|
||||
"wants_dashboard": "$WANT_DASHBOARD",
|
||||
"crew_used": [
|
||||
"mayaeule",
|
||||
"dumbosql",
|
||||
"funkfox",
|
||||
"taichitaube",
|
||||
"tobi",
|
||||
"schnecki",
|
||||
"schraubaer",
|
||||
"snakepy",
|
||||
"pepperphp",
|
||||
"spider",
|
||||
"crabbyrust",
|
||||
"vektor",
|
||||
"asciimonster"
|
||||
],
|
||||
"status": "planned",
|
||||
"learning_areas": [
|
||||
"computer_vision",
|
||||
"opencv",
|
||||
"weather_api",
|
||||
"moon_phases",
|
||||
"machine_learning",
|
||||
"prediction_algorithms"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "📊 Mission geloggt: $LOGDIR/mond_maschine_${TIMESTAMP}.json"
|
||||
echo ""
|
||||
|
||||
cat << "EOF"
|
||||
📚 Nächste Schritte:
|
||||
|
||||
1. 🐍 Python Setup:
|
||||
pip install opencv-python numpy requests ephem
|
||||
|
||||
2. 🌦️ Wetter-API Key besorgen:
|
||||
- OpenWeatherMap: openweathermap.org/api
|
||||
- Kostenloser Account
|
||||
|
||||
3. 📸 Kamera testen:
|
||||
- OpenCV: cv2.VideoCapture(0)
|
||||
- Testbild aufnehmen
|
||||
|
||||
4. 🌙 Mond-Phase berechnen:
|
||||
import ephem
|
||||
moon = ephem.Moon()
|
||||
|
||||
5. 🧠 Vorhersage-Algorithmus implementieren:
|
||||
- Features sammeln
|
||||
- Score berechnen
|
||||
- Threshold setzen
|
||||
|
||||
6. 📊 Dashboard bauen (optional):
|
||||
- Flask/FastAPI Backend
|
||||
- Live-Updates mit WebSocket
|
||||
|
||||
7. 🌈 Warten auf den ersten Regenbogen!
|
||||
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
echo "🦉 Maya-Eule sagt:"
|
||||
echo ""
|
||||
echo " Die schönsten Maschinen sind die,"
|
||||
echo " die uns die Wunder der Natur zeigen."
|
||||
echo " 🌙🌈✨"
|
||||
echo ""
|
||||
|
||||
read -p "🚀 Willst du jetzt bauen? (j/n) " BUILD_NOW
|
||||
|
||||
if [[ "$BUILD_NOW" == "j" ]]; then
|
||||
echo ""
|
||||
echo "🎉 FANTASTISCH! Die Crew ist bereit!"
|
||||
echo ""
|
||||
echo "🌙 Die Mond Maschine nimmt Form an..."
|
||||
echo "💚 Viel Erfolg beim Bauen!"
|
||||
else
|
||||
echo ""
|
||||
echo "💚 Kein Problem! Die Planung ist gespeichert."
|
||||
echo "Die Mond Maschine wartet auf dich! 🌙✨"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "🌲 Zurück zum Wald..."
|
||||
sleep 2
|
||||
@@ -1,52 +0,0 @@
|
||||
from flask import Flask, render_template, Response, request, redirect
|
||||
import cv2
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
def gen_frames():
|
||||
cam = cv2.VideoCapture(0)
|
||||
if not cam.isOpened():
|
||||
print("[WARNUNG] Kamera konnte nicht geöffnet werden.")
|
||||
return
|
||||
|
||||
try:
|
||||
while True:
|
||||
success, frame = cam.read()
|
||||
if not success:
|
||||
break
|
||||
ret, buffer = cv2.imencode('.jpg', frame)
|
||||
frame = buffer.tobytes()
|
||||
yield (b'--frame\r\n'
|
||||
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
||||
finally:
|
||||
cam.release()
|
||||
print("[info] Kamera wurde sauber freigegeben.")
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/video_feed')
|
||||
def video_feed():
|
||||
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
|
||||
|
||||
@app.route('/log_answer', methods=['POST'])
|
||||
def log_answer():
|
||||
user_input = request.form.get('antwort', 'nichts gesagt')
|
||||
mood = request.form.get('mood', 'unspecified')
|
||||
gesture = request.form.get('gesture', 'none')
|
||||
timestamp = datetime.now().isoformat()
|
||||
|
||||
log_entry = {
|
||||
'timestamp': timestamp,
|
||||
'antwort': user_input,
|
||||
'mood': mood,
|
||||
'gesture': gesture
|
||||
}
|
||||
|
||||
with open("snake_log.jsonl", "a") as log_file:
|
||||
log_file.write(json.dumps(log_entry) + "\n")
|
||||
|
||||
return redirect("/")
|
||||
@@ -1,13 +0,0 @@
|
||||
import cv2
|
||||
|
||||
def gen_frames():
|
||||
cap = cv2.VideoCapture(0)
|
||||
while True:
|
||||
success, frame = cap.read()
|
||||
if not success:
|
||||
break
|
||||
else:
|
||||
ret, buffer = cv2.imencode('.jpg', frame)
|
||||
frame = buffer.tobytes()
|
||||
yield (b'--frame\r\n'
|
||||
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
echo "🐍 Starte SnakeCam ..."
|
||||
exec python3 app.py
|
||||
@@ -1,15 +0,0 @@
|
||||
# snakecam_module.py
|
||||
import cv2
|
||||
|
||||
def init_camera(index=0):
|
||||
cam = cv2.VideoCapture(index)
|
||||
if not cam.isOpened():
|
||||
print("[WARNUNG] Kamera konnte nicht geöffnet werden.")
|
||||
return None
|
||||
print("[OK] Kamera erfolgreich geöffnet.")
|
||||
return cam
|
||||
|
||||
def release_camera(cam):
|
||||
if cam and cam.isOpened():
|
||||
cam.release()
|
||||
print("[INFO] Kamera wurde freigegeben.")
|
||||
@@ -1,9 +0,0 @@
|
||||
body {
|
||||
background-color: #f0fff0;
|
||||
font-family: 'Comic Sans MS', cursive, sans-serif;
|
||||
text-align: center;
|
||||
color: #006400;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>🐍 SnakeCam – Krümelblick ins Versteck</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Comic Sans MS', sans-serif;
|
||||
background-color: #e9f5e9;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
img {
|
||||
border: 4px dashed #4caf50;
|
||||
border-radius: 12px;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
form {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
input, select {
|
||||
padding: 0.5rem;
|
||||
font-size: 1rem;
|
||||
margin: 0.5rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
button {
|
||||
padding: 0.7rem 1.2rem;
|
||||
font-size: 1rem;
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #388e3c;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>🐍 SnakeCam</h1>
|
||||
<p>Willkommen kleiner Krümel! Hier siehst du, was deine Kamera entdeckt.</p>
|
||||
|
||||
<!-- Live-Stream -->
|
||||
<img src="/video_feed" alt="Live-Übertragung von SnakeCam 🐍" />
|
||||
|
||||
<!-- Eingabeformular -->
|
||||
<form action="/log_answer" method="POST">
|
||||
<p><strong>Was fühlst du gerade?</strong></p>
|
||||
<input type="text" name="antwort" placeholder="Ich sehe einen ... 🐞🌳🤖" required>
|
||||
|
||||
<p><strong>Wie ist deine Stimmung?</strong></p>
|
||||
<select name="mood">
|
||||
<option value="happy">😊 Glücklich</option>
|
||||
<option value="curious">🤔 Neugierig</option>
|
||||
<option value="calm">😌 Ruhig</option>
|
||||
<option value="excited">😃 Aufgeregt</option>
|
||||
<option value="unspecified">🤷 Keine Angabe</option>
|
||||
</select>
|
||||
|
||||
<p><strong>Hast du eine Geste gemacht?</strong></p>
|
||||
<select name="gesture">
|
||||
<option value="none">🚫 Keine</option>
|
||||
<option value="wave">👋 Winken</option>
|
||||
<option value="thumbs_up">👍 Daumen hoch</option>
|
||||
<option value="peace">✌️ Peace</option>
|
||||
<option value="other">✨ Etwas anderes</option>
|
||||
</select>
|
||||
|
||||
<br><br>
|
||||
<button type="submit">🎯 Eintragen</button>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,73 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from flask import Flask, render_template, Response, request, redirect, url_for
|
||||
import cv2
|
||||
import json
|
||||
from datetime import datetime
|
||||
from gestures.gestures_debug_test import detect_hand_gesture
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Kamera initialisieren
|
||||
cam = cv2.VideoCapture(0)
|
||||
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
|
||||
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
|
||||
|
||||
def gen_frames():
|
||||
while True:
|
||||
success, frame = cam.read()
|
||||
if not success:
|
||||
print("[error] Kein Kamerabild.")
|
||||
break
|
||||
|
||||
# Optional spiegeln (für Selfie-Effekt)
|
||||
frame = cv2.flip(frame, 1)
|
||||
|
||||
# Geste erkennen + ROI anzeigen
|
||||
gesture, roi_coords = detect_hand_gesture(frame)
|
||||
x, y, w, h = roi_coords
|
||||
|
||||
# Rechteck einzeichnen
|
||||
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)
|
||||
cv2.putText(frame, f"Geste: {gesture}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
||||
|
||||
# MJPEG-Streaming
|
||||
ret, buffer = cv2.imencode('.jpg', frame)
|
||||
frame_bytes = buffer.tobytes()
|
||||
yield (b'--frame\r\n'
|
||||
b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n')
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/video_feed')
|
||||
def video_feed():
|
||||
return Response(gen_frames(),
|
||||
mimetype='multipart/x-mixed-replace; boundary=frame')
|
||||
|
||||
@app.route('/log_answer', methods=['POST'])
|
||||
def log_answer():
|
||||
answer = request.form.get("antwort", "").strip()
|
||||
mood = request.form.get("mood", "neutral")
|
||||
gesture = request.form.get("gesture", "none")
|
||||
|
||||
log_entry = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"antwort": answer,
|
||||
"mood": mood,
|
||||
"gesture": gesture
|
||||
}
|
||||
with open("snake_log.jsonl", "a") as logfile:
|
||||
logfile.write(json.dumps(log_entry) + "\n")
|
||||
print(f"[log] Eingeloggt: {log_entry}")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
@app.route('/shutdown')
|
||||
def shutdown():
|
||||
cam.release()
|
||||
print("[info] Kamera wurde sauber freigegeben.")
|
||||
return "Kamera freigegeben."
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Starte SnakeCam ...")
|
||||
app.run(host='0.0.0.0')
|
||||
@@ -1,59 +0,0 @@
|
||||
print("[SnakeCam] Initialisierung beginnt...")
|
||||
from flask import Flask, render_template, Response, request, redirect
|
||||
import cv2
|
||||
import json
|
||||
from datetime import datetime
|
||||
print("[SnakeCam] Imports erfolgreich.")
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
def gen_frames():
|
||||
cam = cv2.VideoCapture(0)
|
||||
if not cam.isOpened():
|
||||
print("[WARNUNG] Kamera konnte nicht geöffnet werden.")
|
||||
return
|
||||
|
||||
try:
|
||||
while True:
|
||||
success, frame = cam.read()
|
||||
if not success:
|
||||
break
|
||||
ret, buffer = cv2.imencode('.jpg', frame)
|
||||
frame = buffer.tobytes()
|
||||
yield (b'--frame\r\n'
|
||||
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
||||
finally:
|
||||
cam.release()
|
||||
print("[info] Kamera wurde sauber freigegeben.")
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/video_feed')
|
||||
def video_feed():
|
||||
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
|
||||
|
||||
@app.route('/log_answer', methods=['POST'])
|
||||
def log_answer():
|
||||
user_input = request.form.get('antwort', 'nichts gesagt')
|
||||
mood = request.form.get('mood', 'unspecified')
|
||||
gesture = request.form.get('gesture', 'none')
|
||||
timestamp = datetime.now().isoformat()
|
||||
|
||||
log_entry = {
|
||||
'timestamp': timestamp,
|
||||
'antwort': user_input,
|
||||
'mood': mood,
|
||||
'gesture': gesture
|
||||
}
|
||||
|
||||
with open("snake_log.jsonl", "a") as log_file:
|
||||
log_file.write(json.dumps(log_entry) + "\n")
|
||||
|
||||
return redirect("/")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("[SnakeCam] Starte Flask Webserver ...")
|
||||
app.run(host='0.0.0.0', port=5000)
|
||||
@@ -1,72 +0,0 @@
|
||||
from flask import Flask, render_template, Response, request, redirect
|
||||
import cv2
|
||||
import datetime
|
||||
import json
|
||||
from gestures_debug import detect_hand_gesture
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
def gen_frames():
|
||||
cam = cv2.VideoCapture(0)
|
||||
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
|
||||
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
|
||||
|
||||
if not cam.isOpened():
|
||||
print("[WARNUNG] Kamera konnte nicht geöffnet werden.")
|
||||
return
|
||||
|
||||
try:
|
||||
while True:
|
||||
success, frame = cam.read()
|
||||
if not success:
|
||||
break
|
||||
|
||||
# Flip horizontal für Spiegelbild
|
||||
frame = cv2.flip(frame, 1)
|
||||
|
||||
# Geste erkennen
|
||||
gesture, roi = detect_hand_gesture(frame)
|
||||
if isinstance(roi, tuple) and len(roi) == 4:
|
||||
x, y, w, h = roi
|
||||
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)
|
||||
cv2.putText(frame, gesture, (x, y - 10),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
||||
|
||||
# Frame in JPEG konvertieren
|
||||
ret, buffer = cv2.imencode('.jpg', frame)
|
||||
frame_bytes = buffer.tobytes()
|
||||
|
||||
# MJPEG-Stream liefern
|
||||
yield (b'--frame\r\n'
|
||||
b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n')
|
||||
finally:
|
||||
cam.release()
|
||||
print("[info] Kamera wurde sauber freigegeben.")
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/video_feed')
|
||||
def video_feed():
|
||||
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
|
||||
|
||||
@app.route('/log_answer', methods=['POST'])
|
||||
def log_answer():
|
||||
data = {
|
||||
"timestamp": datetime.datetime.now().isoformat(),
|
||||
"antwort": request.form.get("antwort", ""),
|
||||
"mood": request.form.get("mood", ""),
|
||||
"gesture": request.form.get("gesture", "")
|
||||
}
|
||||
|
||||
with open("snake_log.jsonl", "a") as f:
|
||||
f.write(json.dumps(data) + "\n")
|
||||
|
||||
return redirect("/")
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("[SnakeCam] Initialisierung beginnt...")
|
||||
print("[SnakeCam] Imports erfolgreich.")
|
||||
print("[SnakeCam] Starte Flask Webserver ...")
|
||||
app.run(host='0.0.0.0', port=5000)
|
||||
@@ -1,77 +0,0 @@
|
||||
# gestures_v3.py
|
||||
import cv2
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
|
||||
# Pfad zum temporären Snapshot zur Diagnose
|
||||
DEBUG_SNAPSHOT_PATH = "/tmp/roi_snapshot.jpg"
|
||||
|
||||
def detect_hand_gesture(frame):
|
||||
"""
|
||||
Erkenne einfache Handgesten wie 'wave' (offene Hand) und 'fist' (geschlossene Faust)
|
||||
durch Analyse der Konturen im unteren rechten Bildbereich.
|
||||
Die Erkennung basiert auf konvexer Hüllenerkennung.
|
||||
|
||||
Args:
|
||||
frame (ndarray): Das aktuelle Kamerabild
|
||||
|
||||
Returns:
|
||||
(str, ndarray): (Geste, Region-of-Interest-Ausschnitt)
|
||||
"""
|
||||
height, width, _ = frame.shape
|
||||
|
||||
# ROI: untere rechte Ecke (¼ des Bildes)
|
||||
roi = frame[int(height * 0.6):height, int(width * 0.6):width]
|
||||
|
||||
# Speichere Snapshot für Debug-Zwecke
|
||||
cv2.imwrite(DEBUG_SNAPSHOT_PATH, roi)
|
||||
|
||||
# Konvertiere zu HSV
|
||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
||||
|
||||
# Hautfarbmaske (angepasst für unterschiedliche Beleuchtung)
|
||||
lower_skin = np.array([0, 30, 60], dtype=np.uint8)
|
||||
upper_skin = np.array([20, 150, 255], dtype=np.uint8)
|
||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
||||
|
||||
# Glätten und Konturen erkennen
|
||||
mask = cv2.GaussianBlur(mask, (7, 7), 0)
|
||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
if contours and len(contours) > 0:
|
||||
# Größte Kontur (Handfläche)
|
||||
contour = max(contours, key=cv2.contourArea)
|
||||
|
||||
# Fehler vermeiden: Fläche zu klein
|
||||
if cv2.contourArea(contour) < 1000:
|
||||
return ("none", roi)
|
||||
|
||||
# Konvexe Hülle und Defekte
|
||||
hull = cv2.convexHull(contour, returnPoints=False)
|
||||
if hull is not None and len(hull) > 3:
|
||||
defects = cv2.convexityDefects(contour, hull)
|
||||
|
||||
if defects is not None:
|
||||
finger_count = 0
|
||||
for i in range(defects.shape[0]):
|
||||
s, e, f, d = defects[i, 0]
|
||||
start = tuple(contour[s][0])
|
||||
end = tuple(contour[e][0])
|
||||
far = tuple(contour[f][0])
|
||||
|
||||
# Abstand analysieren – je mehr Defekte, desto mehr Finger offen
|
||||
a = np.linalg.norm(np.array(start) - np.array(end))
|
||||
b = np.linalg.norm(np.array(start) - np.array(far))
|
||||
c = np.linalg.norm(np.array(end) - np.array(far))
|
||||
angle = np.arccos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c + 1e-5))
|
||||
|
||||
if angle <= np.pi / 2: # < 90°
|
||||
finger_count += 1
|
||||
|
||||
# Auswertung basierend auf Fingeranzahl
|
||||
if finger_count >= 3:
|
||||
return ("wave", roi)
|
||||
elif finger_count == 0:
|
||||
return ("fist", roi)
|
||||
|
||||
return ("none", roi)
|
||||
@@ -1,35 +0,0 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
# --- Handgestenerkennung: Einfacher Hautfarbfilter + Konturanalyse ---
|
||||
def detect_hand_gesture(frame):
|
||||
# Region of Interest (z. B. linke obere Ecke) definieren
|
||||
roi = frame[20:120, 20:120]
|
||||
|
||||
# Konvertiere in HSV-Farbraum
|
||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
||||
|
||||
# Hautfarb-Bereich (kann je nach Licht angepasst werden)
|
||||
lower_skin = np.array([0, 30, 60], dtype=np.uint8)
|
||||
upper_skin = np.array([20, 150, 255], dtype=np.uint8)
|
||||
|
||||
# Maske für Hautfarbe erzeugen
|
||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
||||
|
||||
# Konturen finden
|
||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
gesture = "none"
|
||||
|
||||
# Wenn große Kontur gefunden → einfache „Winken“-Simulation
|
||||
if contours:
|
||||
largest_contour = max(contours, key=cv2.contourArea)
|
||||
if cv2.contourArea(largest_contour) > 1000:
|
||||
gesture = "wave"
|
||||
|
||||
# ROI im Hauptframe markieren
|
||||
cv2.rectangle(frame, (20, 20), (120, 120), (0, 255, 0), 2)
|
||||
cv2.putText(frame, f"Gesture: {gesture}", (20, 160),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
|
||||
|
||||
return gesture, frame
|
||||
@@ -1,50 +0,0 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
def detect_hand_gesture(frame):
|
||||
height, width, _ = frame.shape
|
||||
|
||||
# Dynamische ROI: Mitte des Bildes, zentriert
|
||||
roi_w, roi_h = 200, 200
|
||||
x_start = width // 2 - roi_w // 2
|
||||
y_start = height // 2 - roi_h // 2
|
||||
roi = frame[y_start:y_start + roi_h, x_start:x_start + roi_w]
|
||||
|
||||
# Zeichne das ROI-Fenster im Originalbild
|
||||
cv2.rectangle(frame, (x_start, y_start), (x_start + roi_w, y_start + roi_h), (0, 255, 0), 2)
|
||||
|
||||
# Konvertiere zu HSV-Farbraum für bessere Hauterkennung
|
||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
||||
|
||||
# Hautfarbmaske (getestet für mittlere Hauttöne)
|
||||
lower_skin = np.array([0, 30, 60], dtype=np.uint8)
|
||||
upper_skin = np.array([20, 150, 255], dtype=np.uint8)
|
||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
||||
|
||||
# Glättung und Konturen finden
|
||||
mask = cv2.GaussianBlur(mask, (5, 5), 0)
|
||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
if contours:
|
||||
# Größte Kontur annehmen
|
||||
max_contour = max(contours, key=cv2.contourArea)
|
||||
area = cv2.contourArea(max_contour)
|
||||
|
||||
if area > 2000:
|
||||
# Konvexitätsdefekte (Finger) berechnen
|
||||
hull = cv2.convexHull(max_contour, returnPoints=False)
|
||||
if hull is not None and len(hull) > 3:
|
||||
defects = cv2.convexityDefects(max_contour, hull)
|
||||
finger_count = 0
|
||||
if defects is not None:
|
||||
for i in range(defects.shape[0]):
|
||||
s, e, f, d = defects[i, 0]
|
||||
if d > 10000:
|
||||
finger_count += 1
|
||||
|
||||
if finger_count >= 3:
|
||||
return "wave", roi
|
||||
else:
|
||||
return "fist", roi
|
||||
|
||||
return "none", roi
|
||||
@@ -1,50 +0,0 @@
|
||||
# gestures_debug.py
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
def detect_hand_gesture(frame):
|
||||
height, width, _ = frame.shape
|
||||
|
||||
# --- Fallback-ROI: Mitte des Bildes ---
|
||||
w, h = 100, 100
|
||||
x = width // 2 - w // 2
|
||||
y = height // 2 - h // 2
|
||||
roi = frame[y:y+h, x:x+w]
|
||||
|
||||
if roi.size == 0:
|
||||
print("[warn] ROI leer – kein Bildausschnitt verarbeitet")
|
||||
return "none", (x, y, w, h)
|
||||
|
||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
||||
|
||||
lower_skin = np.array([0, 20, 70], dtype=np.uint8)
|
||||
upper_skin = np.array([20, 255, 255], dtype=np.uint8)
|
||||
|
||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
||||
mask = cv2.dilate(mask, np.ones((3, 3), np.uint8), iterations=4)
|
||||
mask = cv2.GaussianBlur(mask, (5, 5), 100)
|
||||
|
||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
gesture = "none"
|
||||
|
||||
if contours and len(contours) > 0:
|
||||
max_contour = max(contours, key=cv2.contourArea)
|
||||
hull = cv2.convexHull(max_contour, returnPoints=False)
|
||||
if hull is not None and len(hull) > 3:
|
||||
defects = cv2.convexityDefects(max_contour, hull)
|
||||
if defects is not None:
|
||||
cnt_defects = defects.shape[0]
|
||||
if cnt_defects >= 4:
|
||||
gesture = "wave"
|
||||
elif cnt_defects <= 1:
|
||||
gesture = "fist"
|
||||
else:
|
||||
gesture = "unknown"
|
||||
print(f"[debug] Defekte: {len(defects) if defects is not None else 'None'}")
|
||||
else:
|
||||
print("[debug] Keine Konturen erkannt")
|
||||
|
||||
print(f"[result] Geste erkannt: {gesture}")
|
||||
return gesture, (x, y, w, h)
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
def detect_hand_gesture(frame):
|
||||
x, y, w, h = 100, 100, 200, 150
|
||||
roi = frame[y:y+h, x:x+w]
|
||||
|
||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
||||
|
||||
# Hautfarbe grob einschränken
|
||||
lower_skin = np.array([0, 30, 60], dtype=np.uint8)
|
||||
upper_skin = np.array([20, 150, 255], dtype=np.uint8)
|
||||
|
||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
||||
mask = cv2.GaussianBlur(mask, (5, 5), 0)
|
||||
|
||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
gesture = "none"
|
||||
|
||||
if contours:
|
||||
contour = max(contours, key=cv2.contourArea)
|
||||
area = cv2.contourArea(contour)
|
||||
|
||||
if area > 3000:
|
||||
print(f"[debug] Fläche erkannt: {int(area)}")
|
||||
gesture = "hand"
|
||||
|
||||
return gesture, (x, y, w, h)
|
||||
@@ -1,39 +0,0 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
def detect_hand_gesture(frame):
|
||||
x, y, w, h = 100, 100, 150, 150
|
||||
roi = frame[y:y+h, x:x+w]
|
||||
|
||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
||||
|
||||
# einfache Hautfarbe (hell bis leicht gebräunt)
|
||||
lower_skin = np.array([0, 30, 60], dtype=np.uint8)
|
||||
upper_skin = np.array([20, 150, 255], dtype=np.uint8)
|
||||
|
||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
||||
mask = cv2.GaussianBlur(mask, (5, 5), 0)
|
||||
|
||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
gesture = "none"
|
||||
|
||||
if contours:
|
||||
contour = max(contours, key=cv2.contourArea)
|
||||
area = cv2.contourArea(contour)
|
||||
|
||||
if area > 2500:
|
||||
hull = cv2.convexHull(contour, returnPoints=False)
|
||||
if hull is not None and len(hull) > 3:
|
||||
defects = cv2.convexityDefects(contour, hull)
|
||||
if defects is not None:
|
||||
defect_count = defects.shape[0]
|
||||
ratio = float(w) / h
|
||||
|
||||
print(f"[debug] Defekte: {defect_count}, Fläche: {int(area)}, Ratio: {ratio:.2f}")
|
||||
|
||||
# 🌊 einfache Regel für Winken
|
||||
if 3 <= defect_count <= 10 and 2000 < area < 15000 and 1.3 < ratio < 2.3:
|
||||
gesture = "wave"
|
||||
|
||||
return gesture, (x, y, w, h)
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
echo "🐍 Starte SnakeCam ..."
|
||||
python3 app.py
|
||||
@@ -1,62 +0,0 @@
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #111;
|
||||
color: #eee;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 20px;
|
||||
color: #90ee90;
|
||||
}
|
||||
|
||||
.video-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 320px;
|
||||
height: auto;
|
||||
border: 2px solid #555;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
img.mirrored {
|
||||
/* transform: scaleX(-1); Spiegelung horizontal */
|
||||
transform: none;
|
||||
}
|
||||
|
||||
form {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
background: #222;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
label, select, input, button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
input, select {
|
||||
padding: 6px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>🐍 SnakeCam Vision</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #111;
|
||||
color: #eee;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 20px;
|
||||
color: #90ee90;
|
||||
}
|
||||
.video-container {
|
||||
display: inline-block;
|
||||
border: 4px solid #333;
|
||||
background: black;
|
||||
}
|
||||
img {
|
||||
width: 320px;
|
||||
height: 240px;
|
||||
object-fit: cover;
|
||||
}
|
||||
.video-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
form {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
background: #222;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
label, select, input, button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
input, select {
|
||||
padding: 6px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h1>🐍 SnakeCam Vision</h1>
|
||||
|
||||
<div class="video-container">
|
||||
<img src="{{ url_for('video_feed') }}" alt="Live Video Feed">
|
||||
</div>
|
||||
<br clear=all/>
|
||||
<form id="logForm">
|
||||
<p>
|
||||
<label for="antwort">Antwort:</label><br>
|
||||
<input type="text" id="antwort" name="antwort" placeholder="Was denkst du?">
|
||||
</p>
|
||||
<p>
|
||||
<label for="mood">Stimmung:</label><br>
|
||||
<select id="mood" name="mood">
|
||||
<option value="happy">😄 Happy</option>
|
||||
<option value="excited">🤩 Excited</option>
|
||||
<option value="neutral">😐 Neutral</option>
|
||||
<option value="confused">😕 Confused</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="gesture">Geste:</label><br>
|
||||
<select id="gesture" name="gesture">
|
||||
<option value="wave">👋 Wave</option>
|
||||
<option value="fist">✊ Faust</option>
|
||||
<option value="none">🚫 Keine</option>
|
||||
</select>
|
||||
</p>
|
||||
<button type="submit">📝 Speichern</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
document.getElementById("logForm").addEventListener("submit", async function(e) {
|
||||
e.preventDefault(); // Kein Reload!
|
||||
const formData = new FormData(this);
|
||||
await fetch("/log_answer", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
console.log("✅ Antwort wurde gespeichert.");
|
||||
this.reset(); // Eingabefelder leeren
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user