Modernized the web interface with a mobile-first responsive design:
- Added CSS custom properties (variables) for consistent theming
- Implemented responsive breakpoints (640px, 768px) for mobile/tablet/desktop
- Applied fluid typography using clamp() for automatic font scaling
- Enhanced animations (fade-in, slide-in, scale effects) for smooth UX
- Improved component styling with modern shadows and hover effects
- Added backdrop blur effects on modals
- Optimized touch targets (min 44px height) for mobile accessibility
- Updated button groups and layouts to stack on mobile
- Enhanced visual hierarchy with better spacing and typography
- Improved recordings grid with responsive card layout
Template version bumped from 1.9.2 to 2.0.0
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated shutdown and reboot commands to use absolute paths to ensure
they work correctly with the sudoers configuration. Also updated the
sudoers file to allow shutdown with specific arguments.
Changes:
- Use /sbin/shutdown instead of just shutdown
- Use /sbin/reboot instead of just reboot
- Updated sudoers to allow both 'shutdown -h +1' and 'shutdown -h now'
- Ensures commands match sudoers whitelist exactly
The restart was working because reboot might be in PATH, but shutdown
needs the full path to match the sudoers entry.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced literal emoji characters in JavaScript strings with Unicode escape
sequences to prevent encoding issues that cause syntax errors. This resolves
the "Uncaught SyntaxError: Invalid or unexpected token" error at line 2572.
Changes:
- ⚠️ (U+26A0 U+FE0F) → \u26A0\uFE0F in confirm() dialogs
- 🔌 (U+1F50C) → \uD83D\uDD0C in shutdown alert
- 🔄 (U+1F504) → \uD83D\uDD04 in restart alert
- Template version updated to 1.9.2
The emojis still display correctly in the browser, but are now safely
encoded in the JavaScript source to prevent parsing errors.
Fixes: "shutdownSystem is not defined" and "restartSystem is not defined"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed template generation to explicitly use UTF-8 encoding when writing
the HTML template file. This resolves JavaScript syntax errors caused by
emoji characters (⚠️, 🔌, 🔄) in the shutdown/restart confirmation dialogs.
Changes:
- Added encoding='utf-8' parameter to template file write operation
- Updated template version to 1.9.1 to force regeneration
- Ensures all special characters render correctly in browser
Fixes error: "Uncaught SyntaxError: Invalid or unexpected token"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit adds the ability to safely shutdown or restart the Raspberry Pi
from the web interface, with proper sudo permissions and user confirmations.
Features:
- API endpoints for /api/system/shutdown and /api/system/restart
- System Control card in UI with shutdown and restart buttons
- JavaScript confirmation dialogs to prevent accidental shutdowns
- Shutdown has 1-minute delay, restart is immediate
- Auto-reload after restart (30 second delay)
- Sudoers file for passwordless sudo commands
Technical details:
- Uses subprocess.Popen() for non-blocking command execution
- Shutdown: 'sudo shutdown -h +1' (1 minute delay)
- Restart: 'sudo reboot' (immediate)
- Created wedding-phone-shutdown sudoers file
- Template version updated to 1.9.0
Installation required:
sudo cp wedding-phone-shutdown /etc/sudoers.d/wedding-phone-shutdown
sudo chmod 0440 /etc/sudoers.d/wedding-phone-shutdown
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive sorting functionality for recordings:
Backend changes:
- Updated get_recordings() to accept sort_by and sort_order parameters
- Sort options: 'date', 'name', 'duration', 'size'
- Sort order: 'asc' (ascending) or 'desc' (descending)
- Added timestamp field to recordings for accurate date sorting
- Default sort: by date, descending (newest first)
Frontend changes (template v1.8.0):
- Added sort controls above recordings list
- Two dropdowns: sort field and sort direction
- Visual styling with emojis for each option:
📅 Date - Sort by recording date/time
📝 Name - Sort alphabetically by filename
⏱️ Duration - Sort by recording length
💾 Size - Sort by file size
⬇️ Descending / ⬆️ Ascending
- updateSort() JavaScript function reloads page with params
- Preserves selected sort options via query params
URL parameters:
- ?sort=date&order=desc (default)
- ?sort=name&order=asc (alphabetical A-Z)
- ?sort=duration&order=desc (longest first)
- ?sort=size&order=asc (smallest first)
This makes it easy to find specific recordings or organize
them by different criteria depending on what you need.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented automatic config file updates to add missing settings
with default values on startup. This eliminates manual config
editing when upgrading to newer versions.
Features:
- Auto-updates config.json with missing settings
- Auto-updates user_config.json with missing user preferences
- Console logging shows which settings were added
- Preserves existing values - only adds missing keys
- Writes updated config back to file automatically
- Graceful error handling if write fails
System config (config.json):
- Comprehensive defaults for all sections
- GPIO, audio, paths, backup, web, system settings
- New settings like volume_greeting, volume_button, volume_beep
User config (user_config.json):
- Loop-based checking against default_config
- Automatic save when updates are detected
- Error logging for troubleshooting
Benefits:
- Seamless upgrades without manual config edits
- No breaking changes when new features are added
- Users see exactly what was added in console
- Backward compatible with old config files
Example console output:
[CONFIG] Added missing setting: system.volume_beep = 70
[CONFIG] Updated config.json with missing defaults
[USER_CONFIG] Added missing setting: volume_button = 70
[USER_CONFIG] Updated user_config.json with missing defaults
Updated README.md to document auto-update feature.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added individual volume control for each sound type:
- Greeting volume (for welcome messages)
- Button sound volume (for extra button during recording)
- Beep volume (for recording start indicator)
Backend changes:
- Added volume_greeting, volume_button, volume_beep to config
- New getter/setter methods for each volume type
- API endpoints: /api/volume/greeting, /api/volume/button, /api/volume/beep
- Updated play_sound_file() with volume_override parameter
- Greeting, button, and beep playback now use their specific volumes
Frontend changes (template v1.7.0):
- Replaced single volume slider with three separate sliders
- Clean UI with labeled controls for each sound type
- Helper function setupVolumeSlider() for DRY code
- Real-time updates with debouncing (300ms)
- Visual feedback with gradient background
Config changes:
- Added volume_greeting: 70 (default)
- Added volume_button: 70 (default)
- Added volume_beep: 70 (default)
- Backward compatible with existing configs
This allows independent control of each sound's loudness,
useful when greeting needs to be louder than beep, etc.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added detailed console logging throughout button functionality:
Startup logging:
- Shows if button is enabled/disabled
- Displays GPIO pin number and pressed state
- Shows configured button sound file path
Button press detection:
- Logs when button is pressed in main loop
- Logs when button is pressed during recording
- Shows recording/playing state
Button sound playback:
- File path and existence check
- Audio file properties (rate, channels, width)
- Sample rate mismatch warnings
- Playback progress (chunk count)
- Volume level
- Detailed error messages with traceback
Recording loop enhancement:
- Added button checking INSIDE recording loop
- Previously button only checked in main loop
- Button now works during active recording
All logs prefixed with [BUTTON] for easy filtering.
This helps diagnose why button sound isn't playing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added /rename/<filename> API endpoint
- Accepts new_name in JSON body
- Automatically appends .wav extension if missing
- Uses secure_filename() for safety
- Checks for existing files to prevent overwrites
- Backs up renamed file to USB if backup enabled
- Updated web interface (template v1.6.0)
- Added "Rename" button to each recording
- Added renameRecording() JavaScript function
- Uses prompt dialog for name input
- Pre-fills current name (without .wav extension)
- Reloads page after successful rename
This allows users to assign guest names to recordings
for easier organization (e.g., "John_Smith.wav").
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes:
- Update default sample rate from 44.1kHz to 48kHz (more professional standard)
- Add sample rate validation in play_sound_file()
- Display clear warning when uploaded file doesn't match configured rate
- Update default sound generation (dial tone & beep) to use configured RATE
- Prevent playback of incompatible sample rates (avoids ALSA errors)
Error Handling:
- Check file sample rate before attempting playback
- Print warning with actual vs expected sample rate
- Return False and skip playback if mismatch detected
- User-friendly error message with resampling instructions
This fixes the "Invalid sample rate" error when playing files with
non-matching sample rates. Users must now resample their audio files
to match the configured rate (default 48kHz).
To resample audio files:
ffmpeg -i input.wav -ar 48000 output.wav
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major UI Overhaul:
- Replace individual buttons with dropdown selectors
- New "Sound Assignment" card with 3 dropdowns:
* Greeting Sound (plays on pickup)
* Button Sound (plays when button pressed)
* Recording Beep (plays before recording)
- Rename "Greeting Messages" to "Available Sounds"
- Show sound usage badges (⭐🔘📣) in sound list
- Cleaner, more intuitive interface
Benefits:
- Easier to see which sound is assigned where
- One click to change assignments
- Less clutter with fewer buttons
- Shows duration in dropdown options
- Better visual hierarchy
Template v1.5.0 - will auto-regenerate on restart
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend:
- Add beep_sound and beep_enabled configuration options
- Generate default 1000Hz beep (0.5s) on first run
- Add get/set methods for beep sound and enabled state
- Play beep after greeting, before recording starts
- Add /set_beep_sound and /api/beep_enabled endpoints
Frontend:
- Add "Recording Beep" toggle checkbox in settings
- Add "📣 Set Beep" button for each greeting sound
- Show beep status indicator on sounds
- Real-time enable/disable with visual feedback
- Template v1.4.0
Configuration:
- beep_sound: WAV filename for beep (default: "beep.wav")
- beep_enabled: true/false to enable/disable beep
This gives guests a clear audio cue that recording has started.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Split template writing into two parts to avoid f-string parsing issues
- Write version comment separately using f-string
- Write rest of template using regular string (avoids escaping CSS/JS braces)
- Fixes SyntaxError: f-string: expecting '=', or '!', or ':', or '}'
The issue occurred because CSS contains { } braces and % characters that
f-strings try to interpret. Now only the version comment uses f-string.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- 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>
- Add /download_all endpoint to create in-memory ZIP archive
- Include zipfile and io imports for ZIP creation
- Add "Download All as ZIP" button in web interface (only shows when recordings exist)
- ZIP filename includes timestamp: wedding_recordings_YYYYMMDD_HHMMSS.zip
- Only includes .wav files from recordings directory
- Update API documentation in README.md
- Document feature in CLAUDE.md
Allows users to quickly backup all guest recordings in a single download.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace Flask development server with Waitress production WSGI server
- Add waitress>=2.1.0 dependency to pyproject.toml and Makefile
- Configure 4-thread server for better performance and stability
- Create comprehensive CLAUDE.md guide for future development
- Document architecture, deployment, testing, and common patterns
- Update README.md with production-ready feature and dependencies
Eliminates Flask development server warning and provides production-grade
web serving suitable for Raspberry Pi deployment.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:**
- USB drives mounted as root were not writable by regular user
- Caused "Permission denied" errors on backup
- Required running application as root (not secure)
**Solution:**
- Better permission error messages with fix suggestions
- Try to create backup directory first (more flexible)
- Show helpful error: "run sudo chown -R \$USER /media/usb0"
**USB Setup Script (setup_usb.sh):**
- Interactive USB drive mounting
- Automatically detects USB devices
- Mounts with user ownership (uid/gid)
- Tests write permissions
- Shows free space
- Offers to add to /etc/fstab
- Color-coded output
**Documentation Updates:**
- Added 3 methods for mounting with permissions
- Recommended method: mount with uid/gid options
- Added fstab auto-mount example
- Added quick setup script example
- Clear instructions for each method
**Usage:**
```bash
# Easiest method
sudo ./setup_usb.sh
# Or manual mounting
sudo mount -o uid=$(id -u),gid=$(id -g) /dev/sda1 /media/usb0
# Or fix existing mount
sudo chown -R $USER /media/usb0
```
**Security:**
- No need to run wedding phone as root
- User-owned USB mount points
- Proper permission checking
- Clear error messages
**Web Interface:**
- Shows helpful permission error messages
- Includes fix command in error text
- Better UX for permission issues
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**UV Integration:**
- Updated pyproject.toml with [tool.uv.scripts]
- Added 'uv run start' to launch wedding phone
- Added 'uv run test' to run audio tests
- Refactored main code into main() function
- Added proper entry point for package installation
**Systemd Service:**
- Created wedding-phone.service template
- Service runs with UV for dependency management
- Automatic restart on failure
- Proper security hardening (NoNewPrivileges, PrivateTmp)
- GPIO and audio group access configured
**Service Installer:**
- Created install_service.sh automated installer
- Auto-detects project path and user
- Checks for UV installation and dependencies
- Configures service file with correct paths
- Option to enable and start immediately
- Provides helpful command reference
**Installer Features:**
- Validates config.json exists (creates from example if missing)
- Installs UV dependencies automatically
- Updates service file paths dynamically
- Color-coded output for clarity
- Error checking at each step
- Clean installation process
**Usage:**
```bash
# Run directly with UV
uv run start
# Install as system service
./install_service.sh
# Service management
sudo systemctl start wedding-phone
sudo systemctl stop wedding-phone
sudo journalctl -u wedding-phone -f
```
**Documentation:**
- Updated README with UV commands
- Added service installation guide
- Removed old manual systemd instructions
- Added service management commands
- Updated file structure documentation
**Benefits:**
- Easier to run (single command)
- Automatic startup on boot
- Better dependency management
- Professional service integration
- Simplified installation process
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**USB Backup Features:**
- Automatic backup to multiple USB drives
- CRC32 checksum verification for data integrity
- Configurable backup paths in config.json
- Backup on write (recordings and greeting uploads)
- Corrupted backups automatically deleted
- Web interface for monitoring and testing
**Configuration:**
- Added backup section to config.example.json
- enabled: Enable/disable USB backup
- usb_paths: Array of USB mount points
- verify_crc: Enable CRC32 verification
- backup_on_write: Backup immediately after file write
**CRC32 Implementation:**
- calculate_crc32(): Compute file checksum
- 64KB chunk reading for memory efficiency
- Source and destination file verification
- Automatic cleanup of failed copies
**Backup Functions:**
- backup_file_to_usb(): Backup with verification
- get_usb_backup_status(): Check drive status
- Mount detection, write test, free space check
- Preserves directory structure on USB
**Web Interface:**
- USB Backup card with drive status display
- Green/Yellow/Red status indicators
- Free space monitoring
- Test backup button
- Real-time status refresh
- Detailed error reporting
**Integration:**
- Recordings backed up after save
- Greetings backed up after upload
- Backup results logged to console
- Non-blocking backup execution
**API Endpoints:**
- GET /api/backup/status - Drive status
- POST /api/backup/test - Test backup
**Documentation:**
- Complete USB backup guide in README
- Mount instructions for USB drives
- CRC verification explanation
- Backup directory structure
- Web interface usage guide
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Immediate Hook Detection:**
- Playback now checks GPIO pin directly instead of status variable
- Recording checks GPIO pin in every loop iteration
- Both stop immediately when handset is placed back on hook
- No delays or waiting for status updates
**Abort Incomplete Recordings:**
- Added minimum recording duration (1 second)
- Recordings shorter than 1 second are automatically deleted
- Aborted recordings (hook hung up during recording) are not saved
- Failed recordings are cleaned up automatically
- Prevents saving empty or accidental recordings
**Behavior:**
- Pick up handset → Greeting plays
- Hang up during greeting → Greeting stops immediately, no recording
- Hang up during recording < 1s → Recording aborted, file deleted
- Hang up during recording > 1s → Recording saved normally
- Clean state after each hang up, ready for next call
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Button Sound Playback Fix:**
- Use separate PyAudio instance for button sound during recording
- Allows simultaneous audio input (recording) and output (button sound)
- Button sound plays through speaker/handset for guest to hear
- Button sound is NOT captured in the recording file
- Only works during recording phase (not during greeting)
**Button Status Indicator:**
- Added button status display in Phone Status card
- Shows "Button ready" or "Button sound playing..."
- Yellow highlight when button sound is playing
- Status updates dynamically via refresh
- Only visible when extra button is enabled
**Technical Details:**
- Separate audio_playback instance prevents stream conflicts
- Recording uses input stream, button uses output stream
- Both operate on same HiFiBerry device simultaneously
- Status API now includes extra_button_playing flag
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Extra button now only responds during actual recording
- Does not work during greeting playback or when on-hook
- Updated logic from checking off-hook status to checking recording status
- Updated README to reflect recording-only behavior
This allows guests to add sound effects to their recorded message
without interrupting the initial greeting.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Move greeting_delay from system config to user runtime config
- Add GET/POST API endpoints at /api/greeting_delay
- Add delay slider to web interface (0-10 seconds range)
- Implement debounced slider updates (300ms delay)
- Update visual gradient on slider movement
- Display current delay value in seconds
- Allow greeting delay adjustment without editing config files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Breaking Behavior Change:
- Extra button NOW only works when handset is OFF-hook (during call)
- Previous: worked when on-hook (idle) - REVERSED
- Reason: Button is for guests to trigger during their call/recording
New Feature - Greeting Delay:
- Add greeting_delay_seconds to config.json (0-10 seconds)
- Delays greeting playback after handset pickup
- Gives guests time to position phone comfortably
- Default: 0 (plays immediately, backward compatible)
Extra Button Logic:
- Only responds when phone_status == "off_hook"
- Ignored when phone is on-hook (idle)
- Can be pressed during greeting or recording
- Plays entire sound without interruption
- Useful for: "Press button to hear special message"
Greeting Delay Use Cases:
- Wedding: Give guests moment to settle in
- Events: Time to position phone comfortably
- Accessibility: Extra time for elderly guests
- Professional: Pause before message delivery
Configuration:
{
"system": {
"greeting_delay_seconds": 2, // 2 second pause
"extra_button_sound": "surprise.wav"
}
}
Console Output:
- "Extra button ignored - phone is on hook" (when idle)
- "Waiting X seconds before greeting..." (delay active)
- "=== Extra button pressed ===" (during call)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Bug Fixes:
- Extra button now plays sound completely (was stopping immediately)
- Added check_hook_status parameter to play_sound_file()
- Button sounds play with check_hook_status=False (always complete)
- Greeting sounds play with check_hook_status=True (stop if hung up)
Behavior Changes:
- Extra button ONLY works when phone is on-hook (not in use)
- Prevents button from interfering with active calls
- Clear console message when button pressed during call
- Sound plays completely without hook status interruption
Technical Details:
- Modified play_sound_file() to accept check_hook_status parameter
- Changed while loop condition from "and" to conditional break
- Added on-hook check in play_extra_button_sound()
- Greeting playback still stops if handset hung up mid-message
This ensures:
✓ Button sounds always play completely
✓ Button only works when phone not in use
✓ Greeting playback can still be interrupted by hanging up
✓ No audio conflicts between button and phone calls
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Features:
- Configurable extra button on any GPIO pin
- Upload and select custom sound for button press
- Non-blocking button operation (separate thread)
- Debounced to prevent double-triggers (0.5s)
- Works independently from phone handset
- Can be pressed anytime, even during recording
Configuration:
- gpio.extra_button_enabled: Enable/disable feature
- gpio.extra_button_pin: GPIO pin number (default: 27)
- gpio.extra_button_pressed_state: "LOW" or "HIGH"
- system.extra_button_sound: Default sound file
Web Interface:
- Display active button sound in alert
- "🔘 Set Button" action on greeting items
- Visual indicator (🔘) for active button sound
- Upload any WAV file and assign to button
- Play/preview button sounds in browser
Backend:
- RotaryPhone.play_extra_button_sound() method
- RotaryPhone.set_extra_button_sound() method
- Thread-based playback to not block main loop
- /set_extra_button_sound API endpoint
- Extra button sound tracked in user_config.json
Documentation:
- Extra button setup in README
- Configuration examples
- GPIO pin configuration
- Operation workflow
Use Cases:
- Play special message on button press
- Sound effects for wedding games
- Multiple interaction points
- Custom audio triggers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Breaking Changes:
- Configuration now via config.json instead of editing Python code
- Remove all hardcoded paths (no more /home/berwn)
- Separate system config (config.json) from runtime config (user_config.json)
Features:
- config.example.json with all configurable options
- GPIO pin and state configuration
- Audio device index configuration
- Customizable paths (relative or absolute)
- Web port and upload size settings
- No code editing required for deployment
Configuration Structure:
- gpio: hook_pin, hook_pressed_state
- audio: device_index, chunk_size, channels, sample_rate, max_record_seconds
- paths: base_dir, recordings_dir, sounds_dir
- web: port, max_upload_size_mb
- system: active_greeting, default_volume
Script automatically:
- Checks for config.json on startup
- Provides helpful error if missing
- Uses relative paths by default
- Loads test_complete.py config from same file
Updated Documentation:
- Complete configuration guide in README
- Setup instructions without hardcoded paths
- Troubleshooting for config errors
- Device index discovery command
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Features:
- Add pyproject.toml for UV package management
- Volume control with real-time slider (0-100%)
- Backend volume adjustment with numpy audio scaling
- Volume setting persists in config.json
- Debounced API calls for smooth slider interaction
- Enhanced audio playback with volume multiplier
- Update README with UV installation instructions
- Add volume control documentation
API Changes:
- GET /api/volume - Get current volume setting
- POST /api/volume - Set volume level
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Features:
- Web interface for managing rotary phone system
- Support for multiple greeting messages with selector
- Direct audio playback in browser for recordings and greetings
- Upload multiple WAV files at once
- Set active greeting that plays when phone is picked up
- HiFiBerry DAC+ADC Pro audio configuration
- GPIO-based handset detection and audio recording
- Real-time status monitoring with auto-refresh
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>