Add external configuration file system
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>
This commit is contained in:
@@ -15,31 +15,57 @@ import threading
|
||||
from flask import Flask, render_template, request, send_file, jsonify, redirect, url_for
|
||||
from werkzeug.utils import secure_filename
|
||||
import json
|
||||
import sys
|
||||
|
||||
# Configuration
|
||||
HOOK_PIN = 17 # GPIO pin for hook switch (change to your pin)
|
||||
HOOK_PRESSED = GPIO.LOW # Change to GPIO.HIGH if your switch is active high
|
||||
# Load configuration
|
||||
def load_system_config():
|
||||
"""Load system configuration from config.json"""
|
||||
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.json')
|
||||
|
||||
# Check if config exists, otherwise use example
|
||||
if not os.path.exists(config_path):
|
||||
example_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.example.json')
|
||||
if os.path.exists(example_path):
|
||||
print(f"Config file not found. Please copy {example_path} to {config_path}")
|
||||
print("Command: cp config.example.json config.json")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("ERROR: No configuration file found!")
|
||||
sys.exit(1)
|
||||
|
||||
with open(config_path, 'r') as f:
|
||||
return json.load(f)
|
||||
|
||||
# Load system configuration
|
||||
SYS_CONFIG = load_system_config()
|
||||
|
||||
# GPIO Configuration
|
||||
HOOK_PIN = SYS_CONFIG['gpio']['hook_pin']
|
||||
HOOK_PRESSED = GPIO.LOW if SYS_CONFIG['gpio']['hook_pressed_state'] == 'LOW' else GPIO.HIGH
|
||||
|
||||
# Audio settings
|
||||
CHUNK = 1024
|
||||
FORMAT = pyaudio.paInt16
|
||||
CHANNELS = 1
|
||||
RATE = 44100
|
||||
RECORD_SECONDS = 300 # Maximum recording time (5 minutes)
|
||||
AUDIO_DEVICE_INDEX = SYS_CONFIG['audio']['device_index']
|
||||
CHUNK = SYS_CONFIG['audio']['chunk_size']
|
||||
FORMAT = pyaudio.paInt16 # Fixed format
|
||||
CHANNELS = SYS_CONFIG['audio']['channels']
|
||||
RATE = SYS_CONFIG['audio']['sample_rate']
|
||||
RECORD_SECONDS = SYS_CONFIG['audio']['max_record_seconds']
|
||||
|
||||
# Directories
|
||||
BASE_DIR = "/home/berwn/rotary_phone_data"
|
||||
OUTPUT_DIR = os.path.join(BASE_DIR, "recordings")
|
||||
SOUNDS_DIR = os.path.join(BASE_DIR, "sounds")
|
||||
CONFIG_FILE = os.path.join(BASE_DIR, "config.json")
|
||||
DIALTONE_FILE = os.path.join(SOUNDS_DIR, "dialtone.wav") # Legacy default
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
BASE_DIR = os.path.join(SCRIPT_DIR, SYS_CONFIG['paths']['base_dir']) if SYS_CONFIG['paths']['base_dir'].startswith('.') else SYS_CONFIG['paths']['base_dir']
|
||||
BASE_DIR = os.path.abspath(BASE_DIR)
|
||||
OUTPUT_DIR = os.path.join(BASE_DIR, SYS_CONFIG['paths']['recordings_dir'])
|
||||
SOUNDS_DIR = os.path.join(BASE_DIR, SYS_CONFIG['paths']['sounds_dir'])
|
||||
USER_CONFIG_FILE = os.path.join(BASE_DIR, "user_config.json") # Runtime user settings
|
||||
DIALTONE_FILE = os.path.join(SOUNDS_DIR, "dialtone.wav")
|
||||
|
||||
# Web server settings
|
||||
WEB_PORT = 8080
|
||||
WEB_PORT = SYS_CONFIG['web']['port']
|
||||
|
||||
# Flask app
|
||||
app = Flask(__name__)
|
||||
app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024 # 50MB max file size
|
||||
app.config['MAX_CONTENT_LENGTH'] = SYS_CONFIG['web']['max_upload_size_mb'] * 1024 * 1024
|
||||
|
||||
class RotaryPhone:
|
||||
def __init__(self):
|
||||
@@ -64,20 +90,20 @@ class RotaryPhone:
|
||||
self.generate_default_dialtone()
|
||||
|
||||
def load_config(self):
|
||||
"""Load configuration from JSON file"""
|
||||
"""Load user runtime configuration from JSON file"""
|
||||
default_config = {
|
||||
"active_greeting": "dialtone.wav",
|
||||
"active_greeting": SYS_CONFIG['system']['active_greeting'],
|
||||
"greetings": [],
|
||||
"volume": 70 # Default volume percentage (0-100)
|
||||
"volume": SYS_CONFIG['system']['volume']
|
||||
}
|
||||
|
||||
if os.path.exists(CONFIG_FILE):
|
||||
if os.path.exists(USER_CONFIG_FILE):
|
||||
try:
|
||||
with open(CONFIG_FILE, 'r') as f:
|
||||
with open(USER_CONFIG_FILE, 'r') as f:
|
||||
config = json.load(f)
|
||||
# Ensure volume key exists
|
||||
if "volume" not in config:
|
||||
config["volume"] = 70
|
||||
config["volume"] = SYS_CONFIG['system']['volume']
|
||||
return config
|
||||
except:
|
||||
pass
|
||||
@@ -85,8 +111,8 @@ class RotaryPhone:
|
||||
return default_config
|
||||
|
||||
def save_config(self):
|
||||
"""Save configuration to JSON file"""
|
||||
with open(CONFIG_FILE, 'w') as f:
|
||||
"""Save user runtime configuration to JSON file"""
|
||||
with open(USER_CONFIG_FILE, 'w') as f:
|
||||
json.dump(self.config, f, indent=2)
|
||||
|
||||
def get_active_greeting_path(self):
|
||||
@@ -145,13 +171,13 @@ class RotaryPhone:
|
||||
try:
|
||||
wf = wave.open(filepath, 'rb')
|
||||
|
||||
# Use device index 1 for HiFiBerry (change if needed)
|
||||
# Use configured audio device
|
||||
stream = self.audio.open(
|
||||
format=self.audio.get_format_from_width(wf.getsampwidth()),
|
||||
channels=wf.getnchannels(),
|
||||
rate=wf.getframerate(),
|
||||
output=True,
|
||||
output_device_index=1, # HiFiBerry device index
|
||||
output_device_index=AUDIO_DEVICE_INDEX,
|
||||
frames_per_buffer=CHUNK
|
||||
)
|
||||
|
||||
@@ -189,13 +215,13 @@ class RotaryPhone:
|
||||
print(f"Recording to {filename}")
|
||||
|
||||
try:
|
||||
# Use device index 1 for HiFiBerry (change if needed)
|
||||
# Use configured audio device
|
||||
stream = self.audio.open(
|
||||
format=FORMAT,
|
||||
channels=CHANNELS,
|
||||
rate=RATE,
|
||||
input=True,
|
||||
input_device_index=1, # HiFiBerry device index
|
||||
input_device_index=AUDIO_DEVICE_INDEX,
|
||||
frames_per_buffer=CHUNK
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user