diff --git a/README.md b/README.md index 35f3ae8..8d22c36 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,18 @@ This will test: ### 7. Run the System +#### Option A: Run Directly with UV (Recommended) + +```bash +# Run the wedding phone system +uv run start + +# Or run the audio test +uv run test +``` + +#### Option B: Run with Python + ```bash python3 rotary_phone_web.py ``` @@ -149,6 +161,46 @@ The web interface will be available at: - `http://localhost:8080` - `http://:8080` +### 8. Install as System Service (Optional) + +To run the wedding phone automatically on boot: + +```bash +# Run the installer script +./install_service.sh +``` + +The installer will: +- Check dependencies +- Install the systemd service +- Configure for your user and project path +- Optionally enable and start the service + +**Manual service commands:** + +```bash +# Enable service to start on boot +sudo systemctl enable wedding-phone + +# Start service now +sudo systemctl start wedding-phone + +# Stop service +sudo systemctl stop wedding-phone + +# Restart service +sudo systemctl restart wedding-phone + +# View logs (live) +sudo journalctl -u wedding-phone -f + +# View status +sudo systemctl status wedding-phone + +# Disable service +sudo systemctl disable wedding-phone +``` + ## Usage ### Web Interface @@ -282,6 +334,8 @@ wedding-phone/ ├── rotary_phone_web.py # Main application ├── test_complete.py # Audio testing script ├── configure_hifiberry.sh # HiFiBerry setup script +├── install_service.sh # Systemd service installer +├── wedding-phone.service # Systemd service file ├── config.example.json # Example configuration (copy to config.json) ├── pyproject.toml # UV/pip package configuration ├── AUDIO_FIX.md # Audio configuration guide @@ -423,42 +477,6 @@ If the script won't start: 3. Check all paths exist or can be created 4. Verify audio device index is correct -## Auto-start on Boot - -Create a systemd service: - -```bash -sudo nano /etc/systemd/system/wedding-phone.service -``` - -Add (replace `/path/to/wedding-phone` with your actual path): - -```ini -[Unit] -Description=Wedding Phone Service -After=network.target sound.target - -[Service] -Type=simple -User=pi -WorkingDirectory=/path/to/wedding-phone -ExecStart=/usr/bin/python3 /path/to/wedding-phone/rotary_phone_web.py -Restart=always -RestartSec=10 - -[Install] -WantedBy=multi-user.target -``` - -Enable and start: - -```bash -sudo systemctl daemon-reload -sudo systemctl enable wedding-phone.service -sudo systemctl start wedding-phone.service -sudo systemctl status wedding-phone.service -``` - ## API Endpoints The system provides REST API endpoints: diff --git a/install_service.sh b/install_service.sh new file mode 100644 index 0000000..8163efb --- /dev/null +++ b/install_service.sh @@ -0,0 +1,127 @@ +#!/bin/bash +# +# Wedding Phone Service Installer +# Installs the wedding phone as a systemd service +# + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo "==========================================" +echo "Wedding Phone Service Installer" +echo "==========================================" +echo "" + +# Check if running as root +if [ "$EUID" -eq 0 ]; then + echo -e "${RED}ERROR: Do not run this script as root or with sudo${NC}" + echo "This script will prompt for sudo password when needed" + exit 1 +fi + +# Get the directory where this script is located +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SERVICE_FILE="$SCRIPT_DIR/wedding-phone.service" + +echo "Project directory: $SCRIPT_DIR" +echo "" + +# Check if config.json exists +if [ ! -f "$SCRIPT_DIR/config.json" ]; then + echo -e "${YELLOW}WARNING: config.json not found${NC}" + echo "Creating config.json from config.example.json..." + if [ -f "$SCRIPT_DIR/config.example.json" ]; then + cp "$SCRIPT_DIR/config.example.json" "$SCRIPT_DIR/config.json" + echo -e "${GREEN}✓ Created config.json${NC}" + echo -e "${YELLOW}Please edit config.json to configure your system before starting the service${NC}" + echo "" + else + echo -e "${RED}ERROR: config.example.json not found${NC}" + exit 1 + fi +fi + +# Check if UV is installed +if ! command -v uv &> /dev/null; then + echo -e "${RED}ERROR: UV is not installed${NC}" + echo "Install UV with: curl -LsSf https://astral.sh/uv/install.sh | sh" + exit 1 +fi + +echo -e "${GREEN}✓ UV is installed${NC}" + +# Install dependencies +echo "" +echo "Installing dependencies..." +cd "$SCRIPT_DIR" +uv pip install -e . + +echo -e "${GREEN}✓ Dependencies installed${NC}" +echo "" + +# Update the service file with the correct path +echo "Configuring service file..." +TEMP_SERVICE="/tmp/wedding-phone.service" +cat "$SERVICE_FILE" | sed "s|/home/pi/wedding-phone|$SCRIPT_DIR|g" > "$TEMP_SERVICE" + +# Update User in service file +CURRENT_USER=$(whoami) +sed -i "s|User=pi|User=$CURRENT_USER|g" "$TEMP_SERVICE" +sed -i "s|Group=pi|Group=$CURRENT_USER|g" "$TEMP_SERVICE" + +echo -e "${GREEN}✓ Service file configured for user: $CURRENT_USER${NC}" +echo "" + +# Install the service +echo "Installing systemd service..." +sudo cp "$TEMP_SERVICE" /etc/systemd/system/wedding-phone.service +sudo systemctl daemon-reload + +echo -e "${GREEN}✓ Service installed${NC}" +echo "" + +# Ask if user wants to enable and start the service +read -p "Do you want to enable and start the service now? (y/n) " -n 1 -r +echo "" +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Enabling service to start on boot..." + sudo systemctl enable wedding-phone.service + echo -e "${GREEN}✓ Service enabled${NC}" + + echo "" + echo "Starting service..." + sudo systemctl start wedding-phone.service + echo -e "${GREEN}✓ Service started${NC}" + + echo "" + echo "Service status:" + sudo systemctl status wedding-phone.service --no-pager +else + echo "" + echo "Service installed but not started." + echo "To enable and start later, run:" + echo " sudo systemctl enable wedding-phone.service" + echo " sudo systemctl start wedding-phone.service" +fi + +echo "" +echo "==========================================" +echo "Installation Complete!" +echo "==========================================" +echo "" +echo "Useful commands:" +echo " Start service: sudo systemctl start wedding-phone" +echo " Stop service: sudo systemctl stop wedding-phone" +echo " Restart service: sudo systemctl restart wedding-phone" +echo " View logs: sudo journalctl -u wedding-phone -f" +echo " Service status: sudo systemctl status wedding-phone" +echo " Disable service: sudo systemctl disable wedding-phone" +echo "" + +# Clean up +rm -f "$TEMP_SERVICE" diff --git a/pyproject.toml b/pyproject.toml index feb8be3..d841944 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,10 @@ dependencies = [ "RPi.GPIO>=0.7.1", ] +[project.scripts] +wedding-phone = "rotary_phone_web:main" +wedding-phone-test = "test_complete:main" + [project.optional-dependencies] dev = [ "pytest>=7.0.0", @@ -22,3 +26,7 @@ build-backend = "hatchling.build" [tool.uv] dev-dependencies = [] + +[tool.uv.scripts] +start = "python rotary_phone_web.py" +test = "python test_complete.py" diff --git a/rotary_phone_web.py b/rotary_phone_web.py index 803970e..796d58d 100644 --- a/rotary_phone_web.py +++ b/rotary_phone_web.py @@ -873,12 +873,13 @@ def get_local_ip(): except: return "127.0.0.1" -if __name__ == "__main__": +def main(): + """Main entry point for the wedding phone application""" # Create templates directory and HTML template script_dir = os.path.dirname(os.path.abspath(__file__)) templates_dir = os.path.join(script_dir, 'templates') os.makedirs(templates_dir, exist_ok=True) - + # Create the HTML template if it doesn't exist template_file = os.path.join(templates_dir, 'index.html') if not os.path.exists(template_file): @@ -1983,3 +1984,6 @@ if __name__ == "__main__": # Start Flask web server app.run(host='0.0.0.0', port=WEB_PORT, debug=False, threaded=True) + +if __name__ == "__main__": + main() diff --git a/wedding-phone.service b/wedding-phone.service new file mode 100644 index 0000000..247f9b4 --- /dev/null +++ b/wedding-phone.service @@ -0,0 +1,24 @@ +[Unit] +Description=Wedding Phone - Vintage Rotary Phone Audio System +After=network.target sound.target +Wants=network.target sound.target + +[Service] +Type=simple +User=pi +Group=pi +WorkingDirectory=/home/pi/wedding-phone +Environment="PATH=/home/pi/.local/bin:/usr/local/bin:/usr/bin:/bin" +ExecStart=/usr/bin/env uv run start +Restart=always +RestartSec=10 + +# Security hardening +NoNewPrivileges=true +PrivateTmp=true + +# Allow GPIO access +SupplementaryGroups=gpio audio + +[Install] +WantedBy=multi-user.target