Add rotary phone web interface with multiple greeting support

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>
This commit is contained in:
2025-10-24 14:37:20 +07:00
commit 80c45389b2
6 changed files with 1787 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
{
"permissions": {
"allow": [
"Bash(python -m py_compile:*)",
"Bash(git add:*)"
],
"deny": [],
"ask": []
}
}

166
AUDIO_FIX.md Normal file
View File

@@ -0,0 +1,166 @@
# FIX: HiFiBerry Audio Working Now! 🎉
## The Problem
Your HiFiBerry is detected as:
- **Card 3** (not card 0)
- **PyAudio device index 1** (not 0)
The old configuration was trying to use device 0 (the built-in headphone jack), which is why you heard nothing from your speaker.
## ✅ SOLUTION - Quick Fix
### Method 1: Automatic Configuration (Recommended)
Run this script to auto-detect and configure everything:
```bash
cd /home/berwn
chmod +x configure_hifiberry.sh
./configure_hifiberry.sh
```
This will:
1. Auto-detect your HiFiBerry card number
2. Create the correct ~/.asoundrc
3. Set volume to 100%
4. Test the speaker
### Method 2: Manual Configuration
**Step 1: Create correct ALSA config**
```bash
cat > ~/.asoundrc << 'EOF'
pcm.!default {
type asym
playback.pcm "plughw:3,0"
capture.pcm "plughw:3,0"
}
ctl.!default {
type hw
card 3
}
EOF
```
**Step 2: Set volume**
```bash
amixer -c 3 sset Digital 100%
amixer -c 3 sset Analogue 100%
```
**Step 3: Test speaker**
```bash
aplay -D plughw:3,0 /usr/share/sounds/alsa/Front_Center.wav
# OR
speaker-test -D plughw:3,0 -c 1 -t wav
```
### Method 3: Use the Updated Python Script
The updated `rotary_phone_web.py` now uses device index 1 automatically!
Just download the new version and run it:
```bash
cd /home/berwn
python3 rotary_phone_web.py
```
## 🎵 Test Your Speaker Now
After configuration, test with:
```bash
# Using ALSA (command line)
aplay -D plughw:3,0 /usr/share/sounds/alsa/Front_Center.wav
# Using speaker-test
speaker-test -D plughw:3,0 -c 1 -t sine -f 440 -l 3
# Using the Python script
python3 rotary_phone_web.py
# Then pick up the phone handset
```
## 📝 Understanding Your Audio Setup
```
Device 0: bcm2835 Headphones (built-in audio jack)
Device 1: HiFiBerry DAC+ADC Pro ← YOUR SPEAKER IS HERE!
```
**In ALSA terms:**
- Card 0 = bcm2835 (built-in)
- Card 3 = HiFiBerry ← YOUR CARD
**In PyAudio terms:**
- Device index 0 = bcm2835
- Device index 1 = HiFiBerry ← YOUR DEVICE
## ✅ Updated Script Features
The new `rotary_phone_web.py` includes:
1. **Automatic device selection**: Uses device index 1 for HiFiBerry
2. **Both playback and recording**: Configured for your HiFiBerry
3. **Comments for easy changes**: If your device index differs
If your HiFiBerry shows as a different device index, edit these lines in the script:
```python
# Line ~95 - Playback
output_device_index=1, # Change this number if needed
# Line ~135 - Recording
input_device_index=1, # Change this number if needed
```
## 🔧 Optional: Make HiFiBerry Card 0
If you want HiFiBerry to be card 0 (optional), disable the built-in audio:
```bash
sudo nano /boot/firmware/config.txt
# (or /boot/config.txt on older systems)
# Add or uncomment:
dtparam=audio=off
# Make sure this exists:
dtoverlay=hifiberry-dacplusadcpro
# Save and reboot
sudo reboot
```
After reboot, HiFiBerry will be card 0, and you can use the simpler config:
```bash
cat > ~/.asoundrc << 'EOF'
pcm.!default {
type asym
playback.pcm "plughw:0,0"
capture.pcm "plughw:0,0"
}
ctl.!default {
type hw
card 0
}
EOF
```
## 🎉 You're All Set!
Your speaker should now work perfectly with:
- ✅ Command-line tools (aplay, speaker-test)
- ✅ Python script (rotary_phone_web.py)
- ✅ Web interface
- ✅ Recording from microphone
Enjoy your working rotary phone! 📞🎵

Binary file not shown.

100
configure_hifiberry.sh Normal file
View File

@@ -0,0 +1,100 @@
#!/bin/bash
# Auto-detect HiFiBerry and configure ALSA
echo "=========================================="
echo "HiFiBerry Auto-Configuration"
echo "=========================================="
echo ""
# Find HiFiBerry card number
echo "Detecting HiFiBerry..."
CARD_INFO=$(aplay -l | grep -i hifiberry)
if [ -z "$CARD_INFO" ]; then
echo "❌ HiFiBerry not found!"
echo ""
echo "Make sure:"
echo "1. HiFiBerry is properly seated on GPIO pins"
echo "2. /boot/firmware/config.txt has: dtoverlay=hifiberry-dacplusadcpro"
echo "3. You've rebooted after changing config.txt"
exit 1
fi
echo "✓ HiFiBerry found:"
echo "$CARD_INFO"
echo ""
# Extract card number
CARD_NUM=$(echo "$CARD_INFO" | grep -oP 'card \K[0-9]+' | head -1)
echo "Card number: $CARD_NUM"
echo ""
# Create ALSA config
echo "Creating ~/.asoundrc..."
cat > ~/.asoundrc << EOF
# ALSA Configuration for HiFiBerry DAC+ADC Pro
# Auto-generated configuration
pcm.!default {
type asym
playback.pcm "plughw:${CARD_NUM},0"
capture.pcm "plughw:${CARD_NUM},0"
}
ctl.!default {
type hw
card ${CARD_NUM}
}
EOF
echo "✓ Created ~/.asoundrc with card ${CARD_NUM}"
echo ""
# Show the config
echo "Configuration:"
cat ~/.asoundrc
echo ""
# Set volume
echo "Setting volume to 100%..."
amixer -c ${CARD_NUM} sset Master 100% 2>/dev/null || echo "Master control not available"
amixer -c ${CARD_NUM} sset PCM 100% 2>/dev/null || echo "PCM control not available"
amixer -c ${CARD_NUM} sset Digital 100% 2>/dev/null || echo "Digital control not available"
amixer -c ${CARD_NUM} sset Analogue 100% 2>/dev/null || echo "Analogue control not available"
echo ""
# Test speaker
echo "Testing speaker..."
echo "🔊 Playing test tone - you should hear a beep!"
aplay -D plughw:${CARD_NUM},0 /usr/share/sounds/alsa/Front_Center.wav 2>/dev/null || \
speaker-test -D plughw:${CARD_NUM},0 -c 1 -t sine -f 440 -l 2 2>/dev/null
echo ""
echo "=========================================="
echo "Configuration Complete!"
echo "=========================================="
echo ""
echo "Your HiFiBerry is configured as card ${CARD_NUM}"
echo ""
echo "Test commands:"
echo " aplay -D plughw:${CARD_NUM},0 /usr/share/sounds/alsa/Front_Center.wav"
echo " speaker-test -D plughw:${CARD_NUM},0 -c 1 -t wav"
echo ""
echo "For Python script, use device index:"
# Find PyAudio device index
python3 << PYEOF
import pyaudio
audio = pyaudio.PyAudio()
print("")
for i in range(audio.get_device_count()):
info = audio.get_device_info_by_index(i)
if 'hifiberry' in info['name'].lower():
print(f" PyAudio device index: {i}")
print(f" Device name: {info['name']}")
break
audio.terminate()
PYEOF
echo ""

1228
rotary_phone_web.py Normal file

File diff suppressed because it is too large Load Diff

283
test_complete.py Normal file
View File

@@ -0,0 +1,283 @@
#!/usr/bin/env python3
"""
Complete HiFiBerry Test - Verify speaker and microphone work
"""
import pyaudio
import wave
import numpy as np
import time
import os
# HiFiBerry device index (from your system)
HIFIBERRY_INDEX = 1
SAMPLE_RATE = 44100
def test_playback():
"""Test speaker output"""
print("\n" + "="*60)
print("🔊 TESTING SPEAKER PLAYBACK")
print("="*60)
audio = pyaudio.PyAudio()
try:
# Show device info
info = audio.get_device_info_by_index(HIFIBERRY_INDEX)
print(f"\nUsing device: {info['name']}")
print(f"Max output channels: {info['maxOutputChannels']}")
# Generate test tone (440Hz A note)
print("\nGenerating 440Hz test tone...")
duration = 3
t = np.linspace(0, duration, int(SAMPLE_RATE * duration), False)
tone = np.sin(2 * np.pi * 440 * t)
tone = (tone * 0.3 * 32767).astype(np.int16) # 30% volume
# Open stream
stream = audio.open(
format=pyaudio.paInt16,
channels=1,
rate=SAMPLE_RATE,
output=True,
output_device_index=HIFIBERRY_INDEX,
frames_per_buffer=1024
)
print("🎵 Playing 3-second tone - LISTEN NOW!")
print(" You should hear a clear beep from your speaker...")
# Play
chunk_size = 2048
for i in range(0, len(tone.tobytes()), chunk_size):
stream.write(tone.tobytes()[i:i + chunk_size])
stream.stop_stream()
stream.close()
print("✓ Playback completed")
return True
except Exception as e:
print(f"❌ Playback error: {e}")
return False
finally:
audio.terminate()
def test_recording():
"""Test microphone input"""
print("\n" + "="*60)
print("🎙️ TESTING MICROPHONE RECORDING")
print("="*60)
audio = pyaudio.PyAudio()
try:
# Show device info
info = audio.get_device_info_by_index(HIFIBERRY_INDEX)
print(f"\nUsing device: {info['name']}")
print(f"Max input channels: {info['maxInputChannels']}")
# Record
print("\n🔴 Recording for 5 seconds...")
print(" SPEAK NOW or make noise near the microphone...")
stream = audio.open(
format=pyaudio.paInt16,
channels=1,
rate=SAMPLE_RATE,
input=True,
input_device_index=HIFIBERRY_INDEX,
frames_per_buffer=1024
)
frames = []
for i in range(0, int(SAMPLE_RATE / 1024 * 5)):
data = stream.read(1024, exception_on_overflow=False)
frames.append(data)
stream.stop_stream()
stream.close()
print("✓ Recording completed")
# Save to file
filename = "/tmp/test_recording.wav"
wf = wave.open(filename, 'wb')
wf.setnchannels(1)
wf.setsampwidth(audio.get_sample_size(pyaudio.paInt16))
wf.setframerate(SAMPLE_RATE)
wf.writeframes(b''.join(frames))
wf.close()
print(f"✓ Saved to {filename}")
# Calculate volume level
audio_data = np.frombuffer(b''.join(frames), dtype=np.int16)
volume = np.abs(audio_data).mean()
max_volume = np.abs(audio_data).max()
print(f"\nRecording analysis:")
print(f" Average level: {volume:.0f}")
print(f" Peak level: {max_volume}")
if max_volume > 1000:
print(" ✓ Good signal detected!")
else:
print(" ⚠️ Very low signal - microphone might not be working")
audio.terminate()
# Play back
print("\n🔊 Playing back your recording...")
audio = pyaudio.PyAudio()
wf = wave.open(filename, 'rb')
stream = audio.open(
format=audio.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True,
output_device_index=HIFIBERRY_INDEX
)
data = wf.readframes(1024)
while data:
stream.write(data)
data = wf.readframes(1024)
stream.stop_stream()
stream.close()
wf.close()
print("✓ Playback of recording completed")
print(f"\nYou can listen again with: aplay {filename}")
return True
except Exception as e:
print(f"❌ Recording error: {e}")
return False
finally:
audio.terminate()
def test_dial_tone():
"""Test classic dial tone (350Hz + 440Hz)"""
print("\n" + "="*60)
print("📞 TESTING DIAL TONE")
print("="*60)
audio = pyaudio.PyAudio()
try:
print("\nGenerating classic dial tone (350Hz + 440Hz)...")
duration = 3
t = np.linspace(0, duration, int(SAMPLE_RATE * duration), False)
# Generate two frequencies
tone1 = np.sin(2 * np.pi * 350 * t)
tone2 = np.sin(2 * np.pi * 440 * t)
tone = (tone1 + tone2) / 2
tone = (tone * 0.3 * 32767).astype(np.int16)
stream = audio.open(
format=pyaudio.paInt16,
channels=1,
rate=SAMPLE_RATE,
output=True,
output_device_index=HIFIBERRY_INDEX,
frames_per_buffer=1024
)
print("🎵 Playing dial tone - This is what you'll hear when picking up the phone!")
chunk_size = 2048
for i in range(0, len(tone.tobytes()), chunk_size):
stream.write(tone.tobytes()[i:i + chunk_size])
stream.stop_stream()
stream.close()
print("✓ Dial tone playback completed")
return True
except Exception as e:
print(f"❌ Dial tone error: {e}")
return False
finally:
audio.terminate()
def show_device_info():
"""Show all audio devices"""
print("\n" + "="*60)
print("📋 AUDIO DEVICES ON YOUR SYSTEM")
print("="*60)
audio = pyaudio.PyAudio()
print("\nAll devices:")
for i in range(audio.get_device_count()):
info = audio.get_device_info_by_index(i)
device_type = []
if info['maxOutputChannels'] > 0:
device_type.append("OUTPUT")
if info['maxInputChannels'] > 0:
device_type.append("INPUT")
marker = " ← USING THIS" if i == HIFIBERRY_INDEX else ""
print(f" [{i}] {info['name']}{marker}")
print(f" Type: {', '.join(device_type)}")
print(f" Sample rate: {info['defaultSampleRate']}")
print()
audio.terminate()
def main():
"""Main test routine"""
print("\n" + "="*60)
print("🎛️ HIFIBERRY COMPLETE SYSTEM TEST")
print("="*60)
print(f"\nTesting HiFiBerry at device index: {HIFIBERRY_INDEX}")
# Show devices
show_device_info()
input("\nPress Enter to start tests...")
# Test 1: Playback
test1 = test_playback()
time.sleep(1)
# Test 2: Dial tone
test2 = test_dial_tone()
time.sleep(1)
# Test 3: Recording
test3 = test_recording()
# Summary
print("\n" + "="*60)
print("📊 TEST SUMMARY")
print("="*60)
print(f"\n✓ Speaker playback: {'PASS ✓' if test1 else 'FAIL ❌'}")
print(f"✓ Dial tone: {'PASS ✓' if test2 else 'FAIL ❌'}")
print(f"✓ Microphone recording: {'PASS ✓' if test3 else 'FAIL ❌'}")
if test1 and test2 and test3:
print("\n🎉 ALL TESTS PASSED!")
print("\nYour rotary phone system is ready to use!")
print("Run: python3 rotary_phone_web.py")
else:
print("\n⚠️ Some tests failed. Check the output above for details.")
print("\n" + "="*60)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\nTest interrupted by user")
except Exception as e:
print(f"\n❌ Error: {e}")
import traceback
traceback.print_exc()