#!/usr/bin/env bash # Monitor agent - multiplexed serial monitor for AI interaction # Usage: ./scripts/monitor-agent.sh # # Outputs: # - /tmp/doorbell-$BOARD.jsonl - JSON Lines log {"ts":123.456,"line":"..."} # - /tmp/doorbell-$BOARD-state.json - Current device state # - /tmp/doorbell-$BOARD-cmd.fifo - Command pipe (echo 'cmd' > fifo) # # Usage: # tail -f /tmp/doorbell-$BOARD.jsonl # Watch logs # echo 'dashboard' > /tmp/doorbell-$BOARD-cmd.fifo # Send command set -euo pipefail BOARD="${1:-esp32-32e-4}" source "./boards/$BOARD/board-config.sh" source ./scripts/lockfile.sh acquire_lock || exit 1 SERIAL="$PORT" LOGFILE="/tmp/doorbell-$BOARD.jsonl" STATEFILE="/tmp/doorbell-$BOARD-state.json" CMDFIFO="/tmp/doorbell-$BOARD-cmd.fifo" SCREENSOCK="doorbell-$BOARD" # Cleanup cleanup() { screen -S "$SCREENSOCK" -X quit 2>/dev/null || true rm -f "$CMDFIFO" } trap cleanup EXIT # Create command FIFO rm -f "$CMDFIFO" mkfifo "$CMDFIFO" # Initialize state echo '{"screen":"BOOT","deviceState":"BOOTED","backlightOn":false}' > "$STATEFILE" # Start screen in detached mode with logging # -L: enable logging # -Logfile: specify log file # -d -m: create detached # -S: session name screen -L -Logfile "$LOGFILE.raw" -d -m -S "$SCREENSOCK" "$SERIAL" 115200 # Give screen time to start sleep 1 # Reader: parse raw screen log -> JSON Lines + state ( exec >>"$LOGFILE" 2>&1 last_screen_line="" while IFS= read -r line; do # Skip duplicate/corrupted lines [[ "$line" == "$last_screen_line" ]] && continue last_screen_line="$line" # Only process non-empty lines [[ -z "$line" ]] && continue TS=$(date +%s.%3N) printf '{"ts":%s,"line":"%s"}\n' "$TS" "$line" # Parse state changes case "$line" in *"[STATE]"*"→ OFF"*) echo '{"screen":"OFF"}' > "$STATEFILE" ;; *"[STATE]"*"→ DASHBOARD"*) echo '{"screen":"DASHBOARD"}' > "$STATEFILE" ;; *"[STATE]"*"→ ALERT"*) echo '{"screen":"ALERT"}' > "$STATEFILE" ;; *"[STATE]"*"→ BOOT"*) echo '{"screen":"BOOT"}' > "$STATEFILE" ;; *"[ADMIN]"*"dashboard"*) echo '{"screen":"DASHBOARD"}' > "$STATEFILE" ;; *"[ADMIN]"*"off"*) echo '{"screen":"OFF"}' > "$STATEFILE" ;; esac done < "$LOGFILE.raw" ) & READER_PID=$! # Writer: command FIFO -> screen session ( while IFS= read -r cmd < "$CMDFIFO"; do # Send command to screen session screen -S "$SCREENSOCK" -X stuff "$cmd^M" echo "[SENT] $cmd" done ) & WRITER_PID=$! echo "Monitor agent started (BOARD=$BOARD):" echo " Log: $LOGFILE" echo " State: $STATEFILE" echo " Cmd: echo 'dashboard' > $CMDFIFO" echo "" echo "Press Ctrl+C to exit" # Wait for reader wait "$READER_PID" || true