# ═══════════════════════════════════════════════════════════ # Klubhaus Doorbell — Multi-Target Build Harness # ═══════════════════════════════════════════════════════════ # Required tools [tools] hk = "latest" pkl = "latest" # screen = "latest" # Install via system package manager (brew install screen / apt install screen) # zellij = "latest" # Install manually if needed (brew install zellij) # Usage: # BOARD=esp32-32e-4 mise run compile # compile # BOARD=esp32-32e-4 mise run upload # upload # BOARD=esp32-32e-4 mise run monitor # monitor # BOARD=esp32-32e-4 mise run monitor-screen # shows tio command to run manually # # Valid BOARD: esp32-32e, esp32-32e-4, esp32-s3-lcd-43 # Board config lives in: boards/{BOARD}/board-config.sh [tasks.compile] description = "Compile (uses BOARD env var)" depends = ["gen-compile-commands"] run = ''' source ./boards/$BOARD/board-config.sh arduino-cli compile --fqbn "$FQBN" --libraries ./libraries $LIBS --build-property "compiler.cpp.extra_flags=$OPTS" --warnings default ./boards/$BOARD ''' [tasks.upload] description = "Upload (uses BOARD env var)" depends = ["kill"] run = ''' source ./boards/$BOARD/board-config.sh arduino-cli upload --fqbn "$FQBN" --port "$PORT" ./boards/$BOARD ''' [tasks.monitor-raw] depends = ["kill"] run = """ source ./boards/$BOARD/board-config.sh PORT="${PORT:-$PORT}" TARGET="$(readlink -f "$PORT" 2>/dev/null || echo "$PORT")" arduino-cli monitor -p "$TARGET" --config baudrate=115200 """ [tasks.monitor-tio] description = "Show tio command to run in separate terminal" depends = ["kill"] run = """ source ./boards/$BOARD/board-config.sh PORT="${PORT:-$PORT}" TARGET="$(readlink -f "$PORT" 2>/dev/null || echo "$PORT")" tio --map INLCRNL "$TARGET" -e """ [tasks.kill] description = "Kill any process using the serial port for BOARD" run = ''' set +e # Load board config source ./boards/$BOARD/board-config.sh PORT="${PORT:-$PORT}" # Kill any processes using the serial port echo "Killing processes on $PORT..." fuser -k "$PORT" 2>/dev/null || true # Kill monitor-agent Python processes for this board for pid in $(pgrep -f "monitor-agent.py.*$BOARD" 2>/dev/null || true); do echo "Killing monitor-agent.py (PID: $pid)..." kill "$pid" 2>/dev/null || true done # Clean up any stale lockfiles (legacy) rm -f "/tmp/doorbell-${BOARD}.lock" 2>/dev/null || true sleep 1 echo "[OK] Killed processes for $BOARD" exit 0 ''' [tasks.monitor] description = "Monitor agent with JSON log + command pipe (Python-based)" depends = ["kill"] run = """ python3 ./scripts/monitor-agent.py "$BOARD" & """ [tasks.log-tail] description = "Tail the logs with human-readable formatting" run = ''' tail -f "/tmp/doorbell-$BOARD.jsonl" | while read -r line; do ts=$(echo "$line" | python3 -c "import sys,json; print(json.load(sys.stdin)['ts'])" 2>/dev/null) txt=$(echo "$line" | python3 -c "import sys,json; print(json.load(sys.stdin)['line'])" 2>/dev/null) if [[ "$txt" == *"[STATE]"* ]]; then echo -e "\\033[1;35m[$ts]\\033[0m $txt" elif [[ "$txt" == *"[ADMIN]"* ]]; then echo -e "\\033[1;36m[$ts]\\033[0m $txt" elif [[ "$txt" == *"[TOUCH]"* ]]; then echo -e "\\033[1;33m[$ts]\\033[0m $txt" elif [[ "$txt" == *"ALERT"* ]]; then echo -e "\\033[1;31m[$ts]\\033[0m $txt" else echo "[$ts] $txt" fi done ''' [tasks.cmd] description = "Send command to device (COMMAND=dashboard mise run cmd)" run = """ echo -n "$COMMAND" > "/tmp/doorbell-$BOARD-cmd.fifo" echo "[SENT] $COMMAND" """ [tasks.state] description = "Show current device state" run = """ cat "/tmp/doorbell-$BOARD-state.json" """ [tasks.install-libs-shared] description = "Install shared Arduino libraries" run = """ ./scripts/install-shared.sh """ [tasks.install] description = "Install libraries for BOARD (shared + board-specific)" run = """ ./scripts/install-shared.sh ./boards/$BOARD/install.sh """ # Convenience [tasks.clean] description = "Remove build artifacts" run = """ rm -rf vendor/ rm -rf boards/esp32-32e/build rm -rf boards/esp32-32e-4/build rm -rf boards/esp32-s3-lcd-43/build echo "[OK] Build artifacts cleaned" """ [tasks.arduino-clean] description = "Clean Arduino CLI cache (staging + packages)" run = """ echo "Checking ~/.arduino15..." du -sh ~/.arduino15/staging 2>/dev/null || echo "No staging folder" du -sh ~/.arduino15/packages 2>/dev/null || echo "No packages folder" read -p "Delete staging + packages folders? [y/N] " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then rm -rf ~/.arduino15/staging rm -rf ~/.arduino15/packages echo "[OK] Arduino staging + packages cleared" else echo "Aborted" fi """ [tasks.format] run = """ clang-format -i --style=file \ boards/esp32-32e/*.cpp \ boards/esp32-32e/*.h \ boards/esp32-32e/*.ino \ boards/esp32-32e-4/*.cpp \ boards/esp32-32e-4/*.h \ boards/esp32-32e-4/*.ino \ boards/esp32-s3-lcd-43/*.cpp \ boards/esp32-s3-lcd-43/*.h \ boards/esp32-s3-lcd-43/*.ino \ libraries/KlubhausCore/src/*.cpp \ libraries/KlubhausCore/src/*.h \ libraries/KlubhausCore/*.properties """ [tasks.gen-compile-commands] description = "Generate compile_commands.json for LSP (uses BOARD env var)" run = """ rm -rf /tmp/arduino-build source ./boards/$BOARD/board-config.sh arduino-cli compile --only-compilation-database --fqbn "$FQBN" --libraries ./libraries $LIBS --build-property "compiler.cpp.extra_flags=$OPTS" --build-path /tmp/arduino-build ./boards/$BOARD cp /tmp/arduino-build/compile_commands.json . echo "[OK] Generated compile_commands.json for $BOARD" """ [tasks.gen-crush-config] description = "Generate .crush.json with BOARD-based FQBN" run = """ source ./boards/$BOARD/board-config.sh cat > .crush.json << EOF { "lsp": { "arduino": { "command": "arduino-language-server", "args": ["-fqbn", "$FQBN"] }, "cpp": { "command": "clangd" } } } EOF echo "[OK] Generated .crush.json with FQBN: $FQBN" """ [tasks.snap] run = "git add .; lumen draft | git commit -F - " [env] BOARD = "esp32-s3-lcd-43"