Files
wedding-phone/CLAUDE.md
grabowski 4282b0f7ee Add automatic template versioning system
- Add TEMPLATE_VERSION constant (1.3.0) to track UI changes
- Create check_template_version() to compare embedded vs current version
- Embed version marker as HTML comment in generated template
- Auto-regenerate template when version mismatch detected
- Show clear status messages: "Template up-to-date" or "regenerating"
- Document versioning system in CLAUDE.md with usage guidelines

Benefits:
- No manual template deletion required when code updates
- Users automatically get latest UI features on restart
- Clear version tracking for template changes
- Prevents stale template issues

To update template: increment TEMPLATE_VERSION when HTML/CSS/JS changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 12:28:34 +07:00

12 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Wedding Phone is a Raspberry Pi-based rotary phone system for weddings and events. Guests pick up a vintage rotary phone handset to hear a custom greeting, then record voice messages. The system features a Flask web interface for managing greetings and recordings, GPIO hookswitch detection, optional USB backup with CRC32 verification, and HiFiBerry DAC+ADC audio support.

Development Commands

Running the System

# Start the wedding phone system (preferred method)
make start

# Or using UV directly
uv run --no-project python rotary_phone_web.py

# Or using Python directly
python3 rotary_phone_web.py

Testing

# Run complete audio test (speaker + mic + dial tone)
make test

# Or using UV
uv run --no-project python test_complete.py

Dependencies

# Install/sync dependencies using UV (preferred)
make sync

# Or manually with UV
uv pip install flask numpy pyaudio RPi.GPIO

# System dependencies (Raspberry Pi)
sudo apt-get install -y python3-pyaudio portaudio19-dev

Service Management

# Install as systemd service
./install_service.sh

# Service control
sudo systemctl start wedding-phone
sudo systemctl stop wedding-phone
sudo systemctl restart wedding-phone
sudo systemctl status wedding-phone

# View logs
sudo journalctl -u wedding-phone -f

Cleanup

# Clean temporary files and generated templates
make clean

Architecture

Single-File Application Design

The entire Flask application lives in rotary_phone_web.py (~2000 lines). This is intentional - the system is designed for deployment on Raspberry Pi devices where simplicity matters more than strict separation of concerns.

Key architectural components:

  1. Configuration System (lines 22-82)

    • config.json: System configuration (GPIO pins, audio device, paths, backup settings)
    • user_config.json: Runtime user settings (active greeting, volume, button sound)
    • Configuration loaded at startup and used throughout application
    • User settings persisted on changes (volume adjustments, greeting selection)
  2. Backup System with CRC32 Verification (lines 84-218)

    • calculate_crc32(): Compute CRC32 checksum for file verification
    • backup_file_to_usb(): Copy files to multiple USB drives with verification
    • get_usb_backup_status(): Check USB drive mount status and writability
    • Automatic backup on recording/upload if backup_on_write enabled
    • Corrupted backups automatically deleted
  3. RotaryPhone Class (lines 220-550+)

    • Central state machine managing phone lifecycle
    • GPIO event-driven architecture with immediate hook detection
    • Three states: on_hook, off_hook, recording
    • Audio playback with volume control (numpy-based amplitude scaling)
    • Recording with minimum duration requirements (1 second)
    • Extra button support for playing sounds during recording
  4. Flask Web Application (lines 550+)

    • REST API for status, recordings, greetings, volume control
    • Template auto-generation (creates templates/index.html on first run)
    • File upload handling with werkzeug secure_filename
    • Audio streaming endpoints for browser playback

Phone State Machine Flow

1. ON_HOOK (waiting)
   → GPIO detects hookswitch pressed (handset lifted)

2. OFF_HOOK (playing greeting)
   → Optional greeting delay (0-10 seconds)
   → Play active greeting with volume control
   → GPIO continuously checked for hang-up

3. RECORDING (recording audio)
   → Record audio from microphone
   → Extra button can trigger sounds (non-blocking)
   → Stop on hang-up or max duration (300s)
   → Save if ≥1 second, backup to USB if enabled
   → Return to ON_HOOK

Audio Architecture

  • PyAudio Instance: Single shared instance in RotaryPhone.audio
  • Streams: Created per-operation (playback/recording) with configured device index
  • Volume Control: Applied in real-time using numpy array manipulation (int16 scaling)
  • Extra Button Sounds: Separate PyAudio instance to prevent blocking main recording
  • Hook Detection: Direct GPIO reads during playback/recording loops for immediate response

HTML Template Generation and Versioning

The Flask app generates templates/index.html programmatically with automatic version tracking:

Version System:

  • TEMPLATE_VERSION constant (line ~84) tracks template changes
  • Version embedded as HTML comment: <!-- TEMPLATE_VERSION: 1.3.0 -->
  • check_template_version() compares embedded version with current code version
  • Template auto-regenerates on version mismatch

How It Works:

  1. On startup, check_template_version() reads existing template
  2. If version matches TEMPLATE_VERSION, template is up-to-date
  3. If version mismatches or template missing, regeneration occurs automatically
  4. User sees: "Template up-to-date (v1.3.0)" or "Template version mismatch - regenerating"

When to Update:

  • Increment TEMPLATE_VERSION whenever HTML/CSS/JS changes are made
  • Format: "X.Y.Z" (major.minor.patch)
  • Add comment describing the change

Manual Regeneration: If needed, delete templates/ directory and restart - not required with version system

Configuration

config.json Structure

All system configuration is in config.json (copy from config.example.json):

  • gpio: Pin assignments and pressed states (LOW/HIGH) for hookswitch and extra button
  • audio: Device index, sample rate, chunk size, max recording duration
  • paths: Base directory and subdirectories for recordings/sounds
  • backup: USB paths, CRC verification, auto-backup settings
  • web: Port and upload size limits
  • system: Default values for volume, greeting, button sound, greeting delay

Finding Audio Device Index

python3 -c "import pyaudio; p=pyaudio.PyAudio(); [print(f'{i}: {p.get_device_info_by_index(i)[\"name\"]}') for i in range(p.get_device_count())]"

Update config.jsonaudio.device_index with the correct index.

GPIO Configuration

  • hook_pin: BCM GPIO pin number for hookswitch
  • hook_pressed_state: "LOW" if switch pulls to ground when pressed, "HIGH" if pulls to 3.3V
  • extra_button_enabled: true/false to enable optional button feature
  • Pin mode: BCM (not BOARD)
  • Pull-up resistors enabled on inputs

Common Development Tasks

Adding New API Endpoints

Add Flask routes in rotary_phone_web.py. Follow existing patterns:

  • Status endpoints: Return JSON with jsonify()
  • File operations: Use secure_filename() for uploads
  • Audio streaming: Use send_file() with mimetype
  • Error handling: Return appropriate HTTP status codes

Modifying Phone Behavior

Edit the RotaryPhone class methods:

  • play_sound_file(): Audio playback logic with volume/hook checking
  • record_audio(): Recording loop with GPIO monitoring
  • phone_monitor_thread(): Main state machine loop

Adding Configuration Options

  1. Add to config.example.json with documentation
  2. Load in load_system_config() or RotaryPhone.load_config()
  3. Access via SYS_CONFIG (system) or self.config (runtime user settings)
  4. Update README.md configuration section

USB Backup Modifications

  • backup_file_to_usb(): Handles individual file backup
  • CRC verification logic prevents silent corruption
  • Backup called after recording saves and greeting uploads
  • Test with /api/backup/test endpoint

Download All Recordings

The /download_all endpoint creates a ZIP archive of all recordings in memory:

  • Uses zipfile and io.BytesIO() for in-memory ZIP creation
  • Filename includes timestamp: wedding_recordings_YYYYMMDD_HHMMSS.zip
  • Only includes .wav files from recordings directory
  • Returns 404 if no recordings exist
  • Web UI shows "Download All as ZIP" button when recordings are present

Testing

Audio Testing

The test_complete.py script validates:

  1. Speaker output (440Hz test tone)
  2. Dial tone generation (350Hz + 440Hz)
  3. Microphone recording (5-second test)

Run before deployment to verify HiFiBerry configuration.

Manual Testing Checklist

  1. Pick up phone → greeting plays
  2. Hang up during greeting → playback stops immediately
  3. Wait for greeting → recording starts
  4. Speak → recording captures audio
  5. Hang up → recording saves and backs up
  6. Web interface → upload/play/delete functions work
  7. Extra button (if enabled) → sound plays during recording only

Important Patterns

Immediate GPIO Response

The system uses direct GPIO.input() calls inside playback and recording loops rather than callbacks. This ensures immediate response when the handset is hung up:

while data:
    if GPIO.input(HOOK_PIN) != HOOK_PRESSED:
        print("Handset hung up, stopping playback immediately")
        break
    # Continue processing

Volume Control Implementation

Volume is applied to PCM audio data using numpy:

volume = self.get_volume() / 100.0  # 0.0 to 1.0
audio_data = np.frombuffer(data, dtype=np.int16)
audio_data = (audio_data * volume).astype(np.int16)
data = audio_data.tobytes()

File Integrity with CRC32

All file writes (recordings, uploads) can be verified with CRC32:

source_crc = calculate_crc32(source_file)
# ... copy file ...
dest_crc = calculate_crc32(dest_file)
if dest_crc != source_crc:
    # Corrupted, delete backup

Deployment Notes

Raspberry Pi Setup

  1. Install Raspberry Pi OS (Bullseye or newer)
  2. Configure HiFiBerry DAC+ADC (./configure_hifiberry.sh)
  3. Wire GPIO hookswitch and optional button
  4. Create config.json from example
  5. Test audio with make test
  6. Install as service with ./install_service.sh

USB Backup Setup

USB drives must be mounted with user write permissions:

# Automated setup (recommended)
sudo ./setup_usb.sh

# Or manual mount
sudo mount -o uid=$(id -u),gid=$(id -g) /dev/sda1 /media/usb0

HiFiBerry Configuration

The system expects HiFiBerry DAC+ADC Pro or compatible. Configuration includes:

  • Disable onboard audio in /boot/config.txt
  • Enable HiFiBerry overlay
  • Set correct audio device index in config.json See AUDIO_FIX.md for detailed troubleshooting.

Production Web Server

The application uses waitress, a production-ready WSGI server for Python web apps. This eliminates the Flask development server warning and provides:

  • Thread pooling (4 worker threads by default)
  • Better performance and stability
  • Production-safe error handling
  • No "development server" warnings

The server configuration is in rotary_phone_web.py line ~2000:

serve(app, host='0.0.0.0', port=WEB_PORT, threads=4)

To adjust thread count or other waitress settings, modify the serve() call parameters.

Project Structure

wedding-phone/
├── rotary_phone_web.py       # Main application (Flask + GPIO + Audio)
├── test_complete.py           # Audio testing script
├── config.json                # Configuration (copy from config.example.json)
├── config.example.json        # Configuration template
├── Makefile                   # Build commands
├── pyproject.toml             # Python package metadata
├── wedding-phone.service      # Systemd service template
├── install_service.sh         # Service installer
├── setup_usb.sh               # USB setup script
├── configure_hifiberry.sh     # HiFiBerry configuration
├── README.md                  # User documentation
├── CHANGELOG.md               # Version history
├── AUDIO_FIX.md              # Audio troubleshooting
└── templates/                 # Auto-generated on first run
    └── index.html            # Web interface (auto-generated)

Runtime data (auto-created):

rotary_phone_data/           # Configurable via config.json
├── recordings/              # Guest voice recordings
├── sounds/                  # Greeting WAV files
└── user_config.json        # Runtime settings (volume, active greeting)

Dependencies

Core:

  • flask >= 2.3.0 (web framework)
  • numpy >= 1.21.0 (audio processing)
  • pyaudio >= 0.2.13 (audio I/O)
  • RPi.GPIO >= 0.7.1 (GPIO control)
  • waitress >= 2.1.0 (production WSGI server)

System:

  • portaudio19-dev (PyAudio backend)
  • Python 3.8+ (required for Flask 2.3+)

Development:

  • UV package manager (recommended, faster than pip)
  • Make (command shortcuts)

Version Information

Current version: 1.2.0

See CHANGELOG.md for complete version history and upgrade notes.